From 8af59750c601298042c1ea8b856958f0434a1071 Mon Sep 17 00:00:00 2001 From: 3252a8 <3252a8@proton.me> Date: Tue, 12 May 2026 11:20:23 +0300 Subject: [PATCH] fix(tariff-worker): match premium bandwidth stats by username, not uuid --- bot/services/tariff_worker.py | 32 +++++++++++++++++++++++++++++--- tests/test_tariff_worker.py | 22 ++++++++++++++++------ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/bot/services/tariff_worker.py b/bot/services/tariff_worker.py index 029d224..0e43166 100644 --- a/bot/services/tariff_worker.py +++ b/bot/services/tariff_worker.py @@ -78,6 +78,7 @@ class TariffTrafficWorker: panel_data = await self.panel_service.get_user_by_uuid(sub.panel_user_uuid, log_response=False) or {} used, limit, panel_strategy = self.subscription_service._extract_panel_traffic_details(panel_data) panel_status = str(panel_data.get("status") or "").upper() + panel_username = panel_data.get("username") if isinstance(panel_data, dict) else None if used is not None and used != sub.traffic_used_bytes: sub.traffic_used_bytes = used if limit is not None and limit != sub.traffic_limit_bytes: @@ -96,7 +97,7 @@ class TariffTrafficWorker: warning_period_start=warning_period_start if tariff.billing_model == "period" else None, ) - await self._sync_premium_squad_limit(session, sub, tariff, now) + await self._sync_premium_squad_limit(session, sub, tariff, now, panel_username=panel_username) async def _ensure_period_reset_strategy( self, @@ -190,6 +191,8 @@ class TariffTrafficWorker: sub: Subscription, tariff, now: datetime, + *, + panel_username: Optional[str] = None, ) -> None: if not getattr(tariff, "premium_squad_uuids", None): if any( @@ -222,7 +225,13 @@ class TariffTrafficWorker: start_date = now.date().replace(day=1).isoformat() end_date = now.date().isoformat() - premium_used = await self._premium_usage_for_user(sub.panel_user_uuid, node_uuids, start_date, end_date) + premium_used = await self._premium_usage_for_user( + sub.panel_user_uuid, + node_uuids, + start_date, + end_date, + panel_username=panel_username, + ) if premium_used is None: return @@ -379,9 +388,12 @@ class TariffTrafficWorker: node_uuids: list[str], start_date: str, end_date: str, + *, + panel_username: Optional[str] = None, ) -> Optional[int]: total = 0 found = False + username = (panel_username or "").strip() or None for node_uuid in node_uuids: stats = await self.panel_service.get_node_users_bandwidth_stats( node_uuid, @@ -403,7 +415,21 @@ class TariffTrafficWorker: or entry.get("uuid") or entry.get("user_uuid") ) - if entry_uuid != user_uuid: + entry_username = ( + user_obj.get("username") + or entry.get("username") + or entry.get("userUsername") + ) + # Remnawave's /bandwidth-stats/nodes/{uuid}/users response + # currently exposes only {color, username, total}; match by + # username first, fall back to UUID if a future version + # adds it back. + matched = False + if entry_uuid and entry_uuid == user_uuid: + matched = True + elif username and entry_username and entry_username == username: + matched = True + if not matched: continue value = entry.get("total") if value is None: diff --git a/tests/test_tariff_worker.py b/tests/test_tariff_worker.py index 4219d4d..ba08b66 100644 --- a/tests/test_tariff_worker.py +++ b/tests/test_tariff_worker.py @@ -148,7 +148,7 @@ class TariffWorkerTests(unittest.IsolatedAsyncioTestCase): return_value={ "topUsers": [ { - "user": {"uuid": "panel-uuid"}, + "username": "tg_123", "total": 2 * (1024**3), } ] @@ -176,7 +176,13 @@ class TariffWorkerTests(unittest.IsolatedAsyncioTestCase): tariff = settings.tariffs_config.require("standard") with patch("bot.services.tariff_worker.tariff_dal.get_warning", new=AsyncMock(return_value=True)): - await worker._sync_premium_squad_limit(AsyncMock(), sub, tariff, datetime.now(timezone.utc)) + await worker._sync_premium_squad_limit( + AsyncMock(), + sub, + tariff, + datetime.now(timezone.utc), + panel_username="tg_123", + ) self.assertTrue(sub.premium_is_limited) panel_service.update_user_details_on_panel.assert_awaited_once() @@ -205,7 +211,7 @@ class TariffWorkerTests(unittest.IsolatedAsyncioTestCase): return_value={ "topUsers": [ { - "user": {"uuid": "panel-uuid"}, + "username": "tg_123", "total": int(1.5 * (1024**3)), } ] @@ -233,7 +239,9 @@ class TariffWorkerTests(unittest.IsolatedAsyncioTestCase): ) tariff = settings.tariffs_config.require("standard") - await worker._sync_premium_squad_limit(AsyncMock(), sub, tariff, now) + await worker._sync_premium_squad_limit( + AsyncMock(), sub, tariff, now, panel_username="tg_123" + ) self.assertEqual(sub.premium_topup_balance_bytes, int(1.5 * (1024**3))) self.assertEqual(sub.premium_topup_used_bytes, int(0.5 * (1024**3))) @@ -243,14 +251,16 @@ class TariffWorkerTests(unittest.IsolatedAsyncioTestCase): return_value={ "topUsers": [ { - "user": {"uuid": "panel-uuid"}, + "username": "tg_123", "total": int(0.1 * (1024**3)), } ] } ) next_month = datetime(2026, 6, 2, tzinfo=timezone.utc) - await worker._sync_premium_squad_limit(AsyncMock(), sub, tariff, next_month) + await worker._sync_premium_squad_limit( + AsyncMock(), sub, tariff, next_month, panel_username="tg_123" + ) self.assertEqual(sub.premium_topup_balance_bytes, int(1.5 * (1024**3))) self.assertEqual(sub.premium_topup_used_bytes, 0)