diff --git a/handlers/keys/operations/aggregated_links.py b/handlers/keys/operations/aggregated_links.py index 89ee2a9f..849a8331 100644 --- a/handlers/keys/operations/aggregated_links.py +++ b/handlers/keys/operations/aggregated_links.py @@ -39,10 +39,14 @@ async def _try_build_remna_vless(servers: list, email: str) -> tuple[str | None, return None, None, None sub_url = data.get("subscriptionUrl") or None + links = data.get("links") or [] best_vless = None - if sub_url and sub_url.lower().startswith("vless://"): - best_vless = sub_url + for link in links: + if isinstance(link, str) and link.lower().startswith("vless://"): + best_vless = link + logger.debug(f"[Remnawave] Found VLESS in links: {link[:60]}...") + break happ_link = None if happ_cryptolink_enabled and sub_url: diff --git a/handlers/keys/operations/creation.py b/handlers/keys/operations/creation.py index 82af9a17..b846e677 100644 --- a/handlers/keys/operations/creation.py +++ b/handlers/keys/operations/creation.py @@ -6,7 +6,7 @@ from sqlalchemy import update from sqlalchemy.ext.asyncio import AsyncSession from config import PUBLIC_LINK, REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD, SUPERNODE -from database import filter_cluster_by_subgroup, get_servers, get_tariff_by_id, store_key +from database import filter_cluster_by_subgroup, filter_cluster_by_tariff, get_servers, get_tariff_by_id, store_key from database.models import User from handlers.utils import ALLOWED_GROUP_CODES, check_server_key_limit from hooks.processors import process_extract_cryptolink_from_result @@ -96,17 +96,24 @@ async def create_key_on_cluster( traffic_limit_bytes_value = int(traffic_limit_bytes or 0) device_limit_value = int(hwid_limit or 0) - if subgroup_title: - subgroup_servers = await filter_cluster_by_subgroup( + if plan is not None: + filtered = await filter_cluster_by_tariff( + session, enabled_servers, plan, cluster_id + ) + if filtered is not enabled_servers: + enabled_servers = filtered + elif subgroup_title: + enabled_servers = await filter_cluster_by_subgroup( + session, enabled_servers, subgroup_title, cluster_id, tariff_id=plan + ) + elif subgroup_title: + enabled_servers = await filter_cluster_by_subgroup( session, enabled_servers, subgroup_title, cluster_id, tariff_id=plan ) - if subgroup_servers: - enabled_servers = subgroup_servers - else: - logger.warning( - f"[Key Creation] В кластере {cluster_id} не найдено серверов для подгруппы '{subgroup_title}'. " - f"Использую весь кластер." - ) + + if not enabled_servers: + logger.warning(f"[Key Creation] Нет серверов после фильтрации по привязкам в кластере {cluster_id}") + return special = None if is_trial: diff --git a/handlers/keys/operations/renewal.py b/handlers/keys/operations/renewal.py index e2a3682b..098282b5 100644 --- a/handlers/keys/operations/renewal.py +++ b/handlers/keys/operations/renewal.py @@ -8,6 +8,7 @@ from config import REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD, SUPERNODE from database import ( delete_notification, filter_cluster_by_subgroup, + filter_cluster_by_tariff, get_key_details, get_servers, get_tariff_by_id, @@ -15,6 +16,7 @@ from database import ( update_key_expiry, update_key_link, ) +from handlers.utils import ALLOWED_GROUP_CODES from hooks.processors import process_get_cryptolink_after_renewal from logger import ( CLOGGER as logger, @@ -281,7 +283,19 @@ async def renew_key_in_cluster( if single_server: cluster_scope = [single_server] else: - if target_subgroup: + if plan is not None: + filtered = await filter_cluster_by_tariff( + session, cluster, plan, cluster_id + ) + if filtered is not cluster: + cluster_scope = filtered + elif target_subgroup: + cluster_scope = await filter_cluster_by_subgroup( + session, cluster, target_subgroup, cluster_id, tariff_id=plan + ) + else: + cluster_scope = cluster + elif target_subgroup: target = await filter_cluster_by_subgroup( session, cluster, target_subgroup, cluster_id, tariff_id=plan ) @@ -289,6 +303,21 @@ async def renew_key_in_cluster( else: cluster_scope = cluster + if plan is not None: + tariff_for_filter = await get_tariff_by_id(session, plan) + if tariff_for_filter: + gc = (tariff_for_filter.get("group_code") or "").lower() + if gc in ALLOWED_GROUP_CODES: + bound_servers = [s for s in cluster_scope if gc in (s.get("special_groups") or [])] + if bound_servers: + cluster_scope = bound_servers + else: + logger.info(f"[Renewal] Нет серверов со спецгруппой '{gc}' в {cluster_id}") + + if not cluster_scope: + logger.warning(f"[Renewal] Нет серверов после фильтрации в кластере {cluster_id}") + return False + remna_ok = await renew_on_remnawave( cluster=cluster_scope, client_id=client_id, diff --git a/handlers/keys/operations/update.py b/handlers/keys/operations/update.py index f8d6affa..c60e7f0b 100644 --- a/handlers/keys/operations/update.py +++ b/handlers/keys/operations/update.py @@ -6,7 +6,8 @@ from sqlalchemy import delete, select from sqlalchemy.ext.asyncio import AsyncSession from config import PUBLIC_LINK, REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD, SUPERNODE -from database import filter_cluster_by_subgroup, get_servers, get_tariff_by_id, store_key +from database import filter_cluster_by_subgroup, filter_cluster_by_tariff, get_servers, get_tariff_by_id, store_key +from handlers.utils import ALLOWED_GROUP_CODES from database.models import Key, Tariff from handlers.tariffs.tariff_display import GB, get_effective_limits_for_key from handlers.utils import get_least_loaded_cluster @@ -50,14 +51,39 @@ async def update_key_on_cluster( else: raise ValueError(f"Кластер или сервер с ID/именем {cluster_id} не найден.") - if subgroup_code: - filtered = await filter_cluster_by_subgroup( + if tariff_id is not None: + filtered = await filter_cluster_by_tariff( + session, cluster, tariff_id, cluster_id + ) + if filtered is not cluster: + cluster = filtered + elif subgroup_code: + cluster = await filter_cluster_by_subgroup( + session, cluster, subgroup_code, cluster_id, tariff_id=tariff_id + ) + elif subgroup_code: + cluster = await filter_cluster_by_subgroup( session, cluster, subgroup_code, cluster_id, tariff_id=tariff_id ) - if not filtered: - logger.warning(f"[Update] Нет серверов для подгруппы {subgroup_code} в кластере {cluster_id}.") - return client_id, remnawave_link - cluster = filtered + + if not cluster: + logger.warning(f"[Update] Нет серверов после фильтрации по привязкам в кластере {cluster_id}") + return client_id, remnawave_link + + if tariff_id is not None: + tariff = await get_tariff_by_id(session, tariff_id) + if tariff: + gc = (tariff.get("group_code") or "").lower() + if gc in ALLOWED_GROUP_CODES: + bound_servers = [s for s in cluster if gc in (s.get("special_groups") or [])] + if bound_servers: + cluster = bound_servers + else: + logger.info(f"[Update] Нет серверов со спецгруппой '{gc}' в {cluster_id}") + + if not cluster: + logger.warning(f"[Update] Нет серверов после фильтрации по спецгруппам в кластере {cluster_id}") + return client_id, remnawave_link expire_iso = datetime.utcfromtimestamp(expiry_time / 1000).replace(tzinfo=timezone.utc).isoformat() @@ -240,16 +266,41 @@ async def update_subscription( else: cluster_servers = [] - if subgroup_code: - prefiltered = await filter_cluster_by_subgroup( + if tariff_id is not None: + filtered = await filter_cluster_by_tariff( + session, cluster_servers, tariff_id, new_cluster_id + ) + if filtered is not cluster_servers: + cluster_servers = filtered + elif subgroup_code: + cluster_servers = await filter_cluster_by_subgroup( + session, cluster_servers, subgroup_code, new_cluster_id, tariff_id=tariff_id + ) + elif subgroup_code: + cluster_servers = await filter_cluster_by_subgroup( session, cluster_servers, subgroup_code, new_cluster_id, tariff_id=tariff_id ) - if not prefiltered: - logger.warning( - f"[Update] Пересоздание пропущено: нет серверов под подгруппу {subgroup_code} в {new_cluster_id}." - ) - return - cluster_servers = prefiltered + + if not cluster_servers: + logger.warning( + f"[Update] Пересоздание пропущено: нет серверов после фильтрации в {new_cluster_id}." + ) + return + + if tariff: + gc = (getattr(tariff, "group_code", None) or "").lower() + if gc in ALLOWED_GROUP_CODES: + bound_servers = [s for s in cluster_servers if gc in (s.get("special_groups") or [])] + if bound_servers: + cluster_servers = bound_servers + else: + logger.info(f"[Update] Нет серверов со спецгруппой '{gc}' в {new_cluster_id}") + + if not cluster_servers: + logger.warning( + f"[Update] Пересоздание пропущено: нет серверов после фильтрации по спецгруппам в {new_cluster_id}." + ) + return traffic_limit_gb = None device_limit = 0 diff --git a/handlers/notifications/special_notifications.py b/handlers/notifications/special_notifications.py index aa0d7a69..1d08542e 100644 --- a/handlers/notifications/special_notifications.py +++ b/handlers/notifications/special_notifications.py @@ -137,6 +137,12 @@ async def notify_users_no_traffic(bot: Bot, session: AsyncSession, current_time: logger.info("INACTIVE_TRAFFIC_ENABLED <= 0, уведомления о нулевом трафике отключены.") return + trial_tariffs = await get_tariffs(session, group_code="trial") + trial_tariff_ids = {t["id"] for t in trial_tariffs} if trial_tariffs else set() + + if not trial_tariff_ids: + return + remnawave_webapp_enabled = bool(MODES_CONFIG.get("REMNAWAVE_WEBAPP_ENABLED", REMNAWAVE_WEBAPP)) messages = [] @@ -149,6 +155,10 @@ async def notify_users_no_traffic(bot: Bot, session: AsyncSession, current_time: client_id = key.client_id expiry_time = key.expiry_time notified = key.notified + tariff_id = key.tariff_id + + if tariff_id not in trial_tariff_ids: + continue if created_at is None or notified: continue diff --git a/panels/remnawave.cpython-312-x86_64-linux-gnu.so b/panels/remnawave.cpython-312-x86_64-linux-gnu.so index 684a8ac2..4dcb0b83 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