cancel for utm/disable servers for change location/remnawave traffic
This commit is contained in:
@@ -19,7 +19,7 @@ bot = Bot(token=API_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTM
|
||||
storage = MemoryStorage()
|
||||
dp = Dispatcher(bot=bot, storage=storage)
|
||||
|
||||
version = "4.2-b220422"
|
||||
version = "4.2-b220424"
|
||||
|
||||
register_middleware(dp)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ from .keyboard import (
|
||||
build_ads_list_kb,
|
||||
build_ads_stats_kb,
|
||||
build_ads_delete_confirm_kb,
|
||||
build_cancel_input_kb
|
||||
)
|
||||
|
||||
router = Router()
|
||||
@@ -40,7 +41,11 @@ async def handle_ads_menu(callback_query: CallbackQuery):
|
||||
@router.callback_query(AdminAdsCallback.filter(F.action == "create"), IsAdminFilter())
|
||||
async def handle_ads_create(callback_query: CallbackQuery, state: FSMContext):
|
||||
await state.set_state(AdminAdsState.waiting_for_new_name)
|
||||
await callback_query.message.edit_text("📝 Введите <b>название</b> новой ссылки:")
|
||||
await callback_query.message.edit_text(
|
||||
"📝 Введите <b>название</b> новой ссылки:",
|
||||
reply_markup=build_cancel_input_kb()
|
||||
)
|
||||
|
||||
|
||||
|
||||
@router.message(AdminAdsState.waiting_for_new_name, IsAdminFilter())
|
||||
@@ -48,7 +53,11 @@ async def handle_ads_name_input(message: Message, state: FSMContext):
|
||||
name = message.text.strip()
|
||||
await state.update_data(name=name)
|
||||
await state.set_state(AdminAdsState.waiting_for_new_code)
|
||||
await message.answer(f"🔗 Введите <b>код ссылки</b> для: <code>{name}</code>.")
|
||||
await message.answer(
|
||||
f"🔗 Введите <b>код ссылки</b> для: <code>{name}</code>.",
|
||||
reply_markup=build_cancel_input_kb()
|
||||
)
|
||||
|
||||
|
||||
|
||||
@router.message(AdminAdsState.waiting_for_new_code, IsAdminFilter())
|
||||
@@ -153,3 +162,12 @@ def format_ads_stats(stats: dict, username_bot: str) -> str:
|
||||
|
||||
f"<i>Просмотр статистики и управление рекламными ссылками</i>."
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(AdminAdsCallback.filter(F.action == "cancel_input"), IsAdminFilter())
|
||||
async def handle_ads_cancel_input(callback_query: CallbackQuery, state: FSMContext):
|
||||
await state.clear()
|
||||
await callback_query.message.edit_text(
|
||||
text="📊 <b>Аналитика рекламы:</b>",
|
||||
reply_markup=build_ads_kb()
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ def build_ads_kb() -> InlineKeyboardMarkup:
|
||||
builder.button(text="➕ Новая ссылка", callback_data=AdminAdsCallback(action="create").pack())
|
||||
builder.button(text="📊 Список", callback_data=AdminAdsCallback(action="list").pack())
|
||||
builder.row(build_admin_back_btn())
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
@@ -66,4 +67,13 @@ def build_ads_delete_confirm_kb(code: str) -> InlineKeyboardMarkup:
|
||||
callback_data=AdminAdsCallback(action="view", code=code).pack()
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def build_cancel_input_kb() -> InlineKeyboardMarkup:
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.button(
|
||||
text="❌ Отмена",
|
||||
callback_data=AdminAdsCallback(action="cancel_input", code="none").pack()
|
||||
)
|
||||
return builder.as_markup()
|
||||
|
||||
@@ -450,12 +450,26 @@ async def finalize_key_creation(
|
||||
await state.clear()
|
||||
|
||||
|
||||
async def check_server_availability(server_info: dict) -> bool:
|
||||
async def check_server_availability(server_info: dict, session: Any) -> bool:
|
||||
"""
|
||||
Проверяет доступность сервера (3x-ui или Remnawave).
|
||||
Проверяет доступность сервера (3x-ui или Remnawave),
|
||||
а также включён ли он и не превышен ли лимит ключей (max_keys).
|
||||
"""
|
||||
panel_type = server_info.get("panel_type", "3x-ui").lower()
|
||||
server_name = server_info.get("server_name", "unknown")
|
||||
panel_type = server_info.get("panel_type", "3x-ui").lower()
|
||||
enabled = server_info.get("enabled", True)
|
||||
max_keys = server_info.get("max_keys")
|
||||
|
||||
if not enabled:
|
||||
logger.info(f"[Ping] Сервер {server_name} выключен (enabled = FALSE).")
|
||||
return False
|
||||
|
||||
if max_keys is not None:
|
||||
count_query = "SELECT COUNT(*) FROM keys WHERE server_id = $1"
|
||||
key_count = await session.fetchval(count_query, server_name)
|
||||
if key_count >= max_keys:
|
||||
logger.info(f"[Ping] Сервер {server_name} достиг лимита ключей: {key_count}/{max_keys}.")
|
||||
return False
|
||||
|
||||
try:
|
||||
if panel_type == "remnawave":
|
||||
@@ -480,4 +494,4 @@ async def check_server_availability(server_info: dict) -> bool:
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.warning(f"[Ping] Ошибка при проверке сервера {server_name}: {e}")
|
||||
return False
|
||||
return False
|
||||
+30
-26
@@ -551,14 +551,7 @@ async def update_subscription(tg_id: int, email: str, session: Any, cluster_over
|
||||
async def get_user_traffic(session: Any, tg_id: int, email: str) -> dict[str, Any]:
|
||||
"""
|
||||
Получает трафик пользователя на всех серверах, где у него есть ключ (3x-ui и Remnawave).
|
||||
|
||||
Args:
|
||||
session (Any): Сессия базы данных.
|
||||
tg_id (int): ID пользователя Telegram.
|
||||
email (str): Email пользователя.
|
||||
|
||||
Returns:
|
||||
dict[str, Any]: Структура с данными о трафике.
|
||||
Для Remnawave трафик считается один раз и отображается как "Remnawave (общий):".
|
||||
"""
|
||||
query = "SELECT client_id, server_id FROM keys WHERE tg_id = $1 AND email = $2"
|
||||
rows = await session.fetch(query, tg_id, email)
|
||||
@@ -582,6 +575,10 @@ async def get_user_traffic(session: Any, tg_id: int, email: str) -> dict[str, An
|
||||
servers_map = {row["server_name"]: row for row in server_rows}
|
||||
|
||||
user_traffic_data = {}
|
||||
tasks = []
|
||||
|
||||
remnawave_client_id = None
|
||||
remnawave_checked = False
|
||||
|
||||
async def fetch_traffic(server_info: dict, client_id: str) -> tuple[str, Any]:
|
||||
server_name = server_info["server_name"]
|
||||
@@ -598,28 +595,11 @@ async def get_user_traffic(session: Any, tg_id: int, email: str) -> dict[str, An
|
||||
return server_name, round(used_gb, 2)
|
||||
else:
|
||||
return server_name, "Ошибка получения трафика"
|
||||
|
||||
elif panel_type == "remnawave":
|
||||
remna = RemnawaveAPI(api_url)
|
||||
logged_in = await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD)
|
||||
if not logged_in:
|
||||
return server_name, "Не удалось авторизоваться"
|
||||
|
||||
user_data = await remna.get_user_by_uuid(client_id)
|
||||
if not user_data:
|
||||
return server_name, "Клиент не найден"
|
||||
|
||||
used_bytes = user_data.get("usedTrafficBytes", 0)
|
||||
used_gb = used_bytes / 1073741824
|
||||
return server_name, round(used_gb, 2)
|
||||
|
||||
else:
|
||||
return server_name, f"Неизвестная панель: {panel_type}"
|
||||
|
||||
except Exception as e:
|
||||
return server_name, f"Ошибка: {e}"
|
||||
|
||||
tasks = []
|
||||
for row in rows:
|
||||
client_id = row["client_id"]
|
||||
server_id = row["server_id"]
|
||||
@@ -628,12 +608,36 @@ async def get_user_traffic(session: Any, tg_id: int, email: str) -> dict[str, An
|
||||
s for s in servers_map.values() if s["server_name"] == server_id or s["cluster_name"] == server_id
|
||||
]
|
||||
for server_info in matched_servers:
|
||||
tasks.append(fetch_traffic(server_info, client_id))
|
||||
panel_type = server_info.get("panel_type", "3x-ui").lower()
|
||||
|
||||
if panel_type == "remnawave" and not remnawave_checked:
|
||||
remnawave_client_id = client_id
|
||||
remnawave_api_url = server_info["api_url"]
|
||||
remnawave_checked = True
|
||||
elif panel_type == "3x-ui":
|
||||
tasks.append(fetch_traffic(server_info, client_id))
|
||||
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
for server, result in results:
|
||||
user_traffic_data[server] = result
|
||||
|
||||
if remnawave_client_id:
|
||||
try:
|
||||
remna = RemnawaveAPI(remnawave_api_url)
|
||||
if not await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD):
|
||||
user_traffic_data["Remnawave (общий)"] = "Не удалось авторизоваться"
|
||||
else:
|
||||
user_data = await remna.get_user_by_uuid(remnawave_client_id)
|
||||
if not user_data:
|
||||
user_traffic_data["Remnawave (общий)"] = "Клиент не найден"
|
||||
else:
|
||||
used_bytes = user_data.get("usedTrafficBytes", 0)
|
||||
used_gb = round(used_bytes / 1073741824, 2)
|
||||
user_traffic_data["Remnawave (общий)"] = used_gb
|
||||
except Exception as e:
|
||||
user_traffic_data["Remnawave (общий)"] = f"Ошибка: {e}"
|
||||
|
||||
return {"status": "success", "traffic": user_traffic_data}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user