From 49db39c8771b58db039a00a679d78aed9c40b818 Mon Sep 17 00:00:00 2001 From: grendervill Date: Fri, 26 Jun 2026 23:48:32 +0300 Subject: [PATCH] =?UTF-8?q?version=201.2.2:=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=B0=D1=80=D1=81=D0=B8?= =?UTF-8?q?=D0=BD=D0=B3=20=D1=81=D1=82=D0=B0=D1=82=D0=B8=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BA=D0=B8=20=D1=82=D1=80=D0=B0=D1=84=D0=B8=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app.py: get_traffic_stats() — позиционный парсинг 8 колонок mita get users, без сканирования суффиксов - bot.py: cmd_users, cmd_dashboard — единый позиционный парсинг вместо поиска единиц - README: обновлена дорожная карта --- README.md | 2 +- app.py | 63 +++++++++++++++++++------------------------------------ bot.py | 53 +++++++++++++++++++++++----------------------- 3 files changed, 49 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 8656e68..1c99099 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ curl -Ls https://raw.githubusercontent.com/grendervilll/mitactl/main/bootstrap.s - [x] Веб-панель с секретным путём (Flask + Gunicorn, path-based security) - [x] Тёмная тема, адаптивный UI: Dashboard, Users, SSL, Security - [x] CRUD пользователей (создание, удаление, список) — авто/ручное/bulk -- [x] Статистика трафика: день, 7 дней, 30 дней +- [x] Статистика трафика: день, 7 дней, 30 дней (позиционный парсинг mita get users) - [x] Онлайн-статус пользователей - [x] Генерация клиентских конфигов (mieru JSON и sing-box JSON) - [x] Скачивание конфигов и отображение QR-кодов diff --git a/app.py b/app.py index 7f13a8a..e997bc4 100644 --- a/app.py +++ b/app.py @@ -175,35 +175,41 @@ def get_warp_ip(): return "недоступен" def get_traffic_stats(): - """Возвращает трафик и статус активности по всем пользователям.""" + """Возвращает трафик и статус активности по всем пользователям. + Данные берутся напрямую из mita get users.""" raw = mita_cmd("get", "users") week_total = month_total = 0.0 users_stats = [] today = datetime.now().date() - # mita get users output format: + # mita get users output format (8 колонок): # User LastActive 1DayDown 1DayUp 7DaysDown 7DaysUp 30DaysDown 30DaysUp - lines = raw.splitlines() - data_lines = [] - for line in lines: + for line in raw.splitlines(): line = line.strip() if not line or line.upper().startswith("USER"): continue - data_lines.append(line) - - for line in data_lines: - parts = re.split(r"\s{2,}", line.strip()) - if len(parts) < 3: - parts = line.split() - if len(parts) < 3: + parts = line.split() + if len(parts) < 8: continue try: name = parts[0] + d1_down = _parse_traffic(parts[2]) + d1_up = _parse_traffic(parts[3]) + d7_down = _parse_traffic(parts[4]) + d7_up = _parse_traffic(parts[5]) + d30_down = _parse_traffic(parts[6]) + d30_up = _parse_traffic(parts[7]) + + d1_bytes = d1_down + d1_up + d7_bytes = d7_down + d7_up + d30_bytes = d30_down + d30_up + + month_total += d30_bytes + week_total += d7_bytes - # Parse LAST ACTIVE field (parts[1]) - last_active_raw = parts[1].strip() if len(parts) > 1 else "" online = False last_active_display = "никогда" + last_active_raw = parts[1] if last_active_raw and last_active_raw.lower() not in ("never", "-", "n/a", "никогда"): try: la_date = datetime.strptime(last_active_raw[:10], "%Y-%m-%d").date() @@ -222,38 +228,13 @@ def get_traffic_stats(): except Exception: last_active_display = last_active_raw - # Find traffic columns — contain units like MiB, GiB, KiB, B - # mita output: 1DayDown 1DayUp 7DaysDown 7DaysUp 30DaysDown 30DaysUp - traffic_parts = [p for p in parts[1:] if any(u in p for u in ["TiB","GiB","MiB","KiB","iB","B"])] - d1_down = d1_up = d7_down = d7_up = d30_down = d30_up = 0.0 - if len(traffic_parts) >= 6: - d1_down = _parse_traffic(traffic_parts[0]) - d1_up = _parse_traffic(traffic_parts[1]) - d7_down = _parse_traffic(traffic_parts[2]) - d7_up = _parse_traffic(traffic_parts[3]) - d30_down = _parse_traffic(traffic_parts[4]) - d30_up = _parse_traffic(traffic_parts[5]) - elif len(traffic_parts) >= 4: - d1_down = _parse_traffic(traffic_parts[0]) - d1_up = _parse_traffic(traffic_parts[1]) - d30_down = _parse_traffic(traffic_parts[2]) - d30_up = _parse_traffic(traffic_parts[3]) - elif len(traffic_parts) >= 2: - d30_down = _parse_traffic(traffic_parts[0]) - d30_up = _parse_traffic(traffic_parts[1]) - else: - continue - - d7_bytes = d7_down + d7_up if (d7_down + d7_up) > 0 else (d30_down + d30_up) / 30 * 7 - month_total += d30_down + d30_up - week_total += d7_bytes users_stats.append({ "name": name, "online": online, "last_active": last_active_display, - "day_mb": round((d1_down + d1_up) / 1024 / 1024, 2), + "day_mb": round(d1_bytes / 1024 / 1024, 2), "week_mb": round(d7_bytes / 1024 / 1024, 2), - "month_mb": round((d30_down + d30_up) / 1024 / 1024, 2), + "month_mb": round(d30_bytes / 1024 / 1024, 2), }) except Exception: continue diff --git a/bot.py b/bot.py index d93e07c..967f9e0 100644 --- a/bot.py +++ b/bot.py @@ -149,26 +149,25 @@ async def cmd_users(update: Update, context: ContextTypes.DEFAULT_TYPE): stats_map = {} for line in data_lines: - parts = re.split(r"\s{2,}", line.strip()) - if len(parts) < 3: - parts = line.split() - if len(parts) < 3: + parts = line.split() + if len(parts) < 8: continue try: name = parts[0] - traffic_parts = [p for p in parts[1:] if any(u in p for u in ["TiB","GiB","MiB","KiB","iB","B"])] - d1 = d30 = 0.0 - if len(traffic_parts) >= 6: - d1 = _parse_traffic(traffic_parts[0]) + _parse_traffic(traffic_parts[1]) - d30= _parse_traffic(traffic_parts[4]) + _parse_traffic(traffic_parts[5]) - elif len(traffic_parts) >= 4: - d1 = _parse_traffic(traffic_parts[0]) + _parse_traffic(traffic_parts[1]) - d30= _parse_traffic(traffic_parts[2]) + _parse_traffic(traffic_parts[3]) - elif len(traffic_parts) >= 2: - d30= _parse_traffic(traffic_parts[0]) + _parse_traffic(traffic_parts[1]) + + d1_down = _parse_traffic(parts[2]) + d1_up = _parse_traffic(parts[3]) + d7_down = _parse_traffic(parts[4]) + d7_up = _parse_traffic(parts[5]) + d30_down = _parse_traffic(parts[6]) + d30_up = _parse_traffic(parts[7]) + + d1_bytes = d1_down + d1_up + d7_bytes = d7_down + d7_up + d30_bytes = d30_down + d30_up online = False - la_raw = parts[1].strip() if len(parts) > 1 else "" + la_raw = parts[1] if la_raw and la_raw.lower() not in ("never","-","n/a","никогда"): try: la_date = datetime.strptime(la_raw[:10], "%Y-%m-%d").date() @@ -177,8 +176,10 @@ async def cmd_users(update: Update, context: ContextTypes.DEFAULT_TYPE): except Exception: pass - stats_map[name] = {"online": online, "day_mb": round(d1/1024/1024, 2), - "month_mb": round(d30/1024/1024, 2)} + stats_map[name] = {"online": online, + "day_mb": round(d1_bytes/1024/1024, 2), + "week_mb": round(d7_bytes/1024/1024, 2), + "month_mb": round(d30_bytes/1024/1024, 2)} except Exception: continue @@ -346,18 +347,16 @@ async def cmd_dashboard(update: Update, context: ContextTypes.DEFAULT_TYPE): traffic = None try: raw = mita_cmd("get", "users") - d_lines = [l.strip() for l in raw.splitlines() if l.strip() and not l.upper().startswith("USER")] week_total = month_total = 0.0 - for line in d_lines: - parts = re.split(r"\s{2,}", line.strip()) - if len(parts) < 3: - parts = line.split() - if len(parts) < 3: + for line in raw.splitlines(): + line = line.strip() + if not line or line.upper().startswith("USER"): continue - tp = [p for p in parts[1:] if any(u in p for u in ["TiB","GiB","MiB","KiB","iB","B"])] - if len(tp) >= 6: - week_total += _parse_traffic(tp[2]) + _parse_traffic(tp[3]) - month_total += _parse_traffic(tp[4]) + _parse_traffic(tp[5]) + parts = line.split() + if len(parts) < 8: + continue + week_total += _parse_traffic(parts[4]) + _parse_traffic(parts[5]) + month_total += _parse_traffic(parts[6]) + _parse_traffic(parts[7]) traffic = (round(week_total/1024**3, 2), round(month_total/1024**3, 2)) except Exception: pass