diff --git a/handlers/admin/ads/ads_handler.py b/handlers/admin/ads/ads_handler.py
index 58ffac19..d7e254a6 100644
--- a/handlers/admin/ads/ads_handler.py
+++ b/handlers/admin/ads/ads_handler.py
@@ -4,6 +4,7 @@ from aiogram.fsm.state import State, StatesGroup
from aiogram.types import CallbackQuery, Message
from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession
+import re
from config import USERNAME_BOT
from database import create_tracking_source, get_tracking_source_stats
@@ -50,7 +51,8 @@ async def handle_ads_name_input(message: Message, state: FSMContext):
await state.update_data(name=name)
await state.set_state(AdminAdsState.waiting_for_new_code)
await message.answer(
- f"🔗 Введите код ссылки для: {name}.",
+ f"🔗 Введите код ссылки для: {name}.\n\n"
+ f"💡 Правила: только латинские буквы и цифры",
reply_markup=build_cancel_input_kb(),
)
@@ -62,6 +64,15 @@ async def handle_ads_code_input(
code = message.text.strip()
data = await state.get_data()
name = data["name"]
+
+ if not re.match(r'^[a-zA-Z0-9]+$', code):
+ await message.answer(
+ "❌ Код может содержать только латинские буквы и цифры\n"
+ "Введите код заново:",
+ reply_markup=build_cancel_input_kb(),
+ )
+ return
+
code_with_prefix = f"utm_{code}"
try:
diff --git a/handlers/admin/management/management_handler.py b/handlers/admin/management/management_handler.py
index 76e7c8b6..65dd9424 100644
--- a/handlers/admin/management/management_handler.py
+++ b/handlers/admin/management/management_handler.py
@@ -12,6 +12,7 @@ import os, subprocess, sys
import json
from aiogram import Bot
from panels.remnawave import RemnawaveAPI
+from panels.remnawave_time import get_all_users_time, login_remnawave
from tempfile import NamedTemporaryFile
import traceback
from datetime import datetime
@@ -426,16 +427,32 @@ async def show_remnawave_clients(callback: CallbackQuery, session: AsyncSession)
return
server = servers[0]
- api = RemnawaveAPI(base_url=server.api_url)
+# api = RemnawaveAPI(base_url=server.api_url)
+#
+# if not await api.login(username=REMNAWAVE_LOGIN, password=REMNAWAVE_PASSWORD):
+# await callback.message.edit_text(
+# "❌ Не удалось авторизоваться на Remnawave панели.",
+# reply_markup=build_back_to_db_menu(),
+# )
+# return
+#
+# users = await api.get_all_users()
+# if not users:
+# await callback.message.edit_text(
+# "📭 На панели нет клиентов.",
+# reply_markup=build_back_to_db_menu(),
+# )
+# return
- if not await api.login(username=REMNAWAVE_LOGIN, password=REMNAWAVE_PASSWORD):
+ token = await login_remnawave(server.api_url, REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD)
+ if not token:
await callback.message.edit_text(
"❌ Не удалось авторизоваться на Remnawave панели.",
reply_markup=build_back_to_db_menu(),
)
return
- users = await api.get_all_users()
+ users = await get_all_users_time(server.api_url, REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD)
if not users:
await callback.message.edit_text(
"📭 На панели нет клиентов.",
@@ -560,4 +577,4 @@ async def import_remnawave_keys(session: AsyncSession, users: list[dict], server
await session.commit()
logger.info(f"[IMPORT] Всего добавлено ключей: {added}")
- return added
+ return added
\ No newline at end of file
diff --git a/panels/remnawave_time.py b/panels/remnawave_time.py
new file mode 100644
index 00000000..12218469
--- /dev/null
+++ b/panels/remnawave_time.py
@@ -0,0 +1,98 @@
+import aiohttp
+from typing import List, Dict, Any
+from logger import logger
+from config import REMNAWAVE_TOKEN_LOGIN_ENABLED, REMNAWAVE_ACCESS_TOKEN, REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD
+
+
+async def login_remnawave(api_url: str, username: str, password: str) -> str | None:
+ if REMNAWAVE_TOKEN_LOGIN_ENABLED and REMNAWAVE_ACCESS_TOKEN:
+ logger.info("[Remnawave API] Используется авторизация по токену")
+ return REMNAWAVE_ACCESS_TOKEN
+
+ async with aiohttp.ClientSession() as session:
+ auth_data = {
+ "username": username,
+ "password": password
+ }
+
+ try:
+ auth_response = await session.post(f"{api_url}/auth/login", json=auth_data)
+
+ if auth_response.status != 200:
+ logger.error(f"[Remnawave API] Ошибка HTTP статуса: {auth_response.status}")
+ return None
+
+ auth_result = await auth_response.json()
+ token = None
+ if auth_result.get("success") and auth_result.get("data", {}).get("token"):
+ token = auth_result.get("data", {}).get("token")
+ elif auth_result.get("response", {}).get("accessToken"):
+ token = auth_result.get("response", {}).get("accessToken")
+ elif auth_result.get("token"):
+ token = auth_result.get("token")
+
+ if not token:
+ logger.error(f"[Remnawave API] Токен не найден в ответе")
+
+ return token
+
+ except Exception as e:
+ logger.error(f"[Remnawave API] Ошибка при авторизации: {e}")
+ return None
+
+
+async def get_all_users_time(api_url: str, username: str, password: str) -> List[Dict[str, Any]]:
+ all_users = []
+ page_size = 250
+ start = 0
+
+ token = await login_remnawave(api_url, username, password)
+ if not token:
+ logger.error("[Remnawave API] Не удалось получить токен авторизации")
+ return []
+
+ headers = {"Authorization": f"Bearer {token}"}
+
+ async with aiohttp.ClientSession() as session:
+ while True:
+ params = {
+ "size": page_size,
+ "start": start
+ }
+
+ users_endpoint = f"{api_url}/users"
+
+ users_response = await session.get(users_endpoint, params=params, headers=headers)
+
+ if users_response.status != 200:
+ break
+
+ users_result = await users_response.json()
+
+ if users_result.get("success") is False:
+ break
+
+ users_data = None
+ if "response" in users_result:
+ users_data = users_result.get("response", {})
+ elif "data" in users_result:
+ users_data = users_result.get("data", {})
+ else:
+ users_data = users_result
+
+ if not users_data:
+ break
+
+ users = users_data.get("users", [])
+ total = users_data.get("total", 0)
+
+ if not users:
+ break
+
+ all_users.extend(users)
+ start += len(users)
+
+ if len(users) < page_size or start >= total:
+ break
+
+ return all_users
\ No newline at end of file