from aiogram import F, Router, types
from aiogram.types import CallbackQuery
from sqlalchemy.ext.asyncio import AsyncSession
from panels.remnawave_runtime import (
invalidate_remnawave_profile,
resolve_remnawave_api_url,
with_remnawave_api,
)
from filters.admin import IsAdminFilter
from .keyboard import AdminUserEditorCallback, build_editor_kb, build_hwid_menu_kb
from .utils import resolve_admin_key
router = Router()
@router.callback_query(
AdminUserEditorCallback.filter(F.action == "users_hwid_menu"),
IsAdminFilter(),
)
async def handle_hwid_menu(
callback_query: CallbackQuery,
callback_data: AdminUserEditorCallback,
session: AsyncSession,
):
key_ref = str(callback_data.data)
tg_id = callback_data.tg_id
key_obj = await resolve_admin_key(session, tg_id, key_ref)
if not key_obj:
await callback_query.message.edit_text("🚫 Не удалось найти ключ.", reply_markup=build_editor_kb(tg_id))
return
client_id = key_obj.client_id
remna_api_url = await resolve_remnawave_api_url(session, "", fallback_any=True)
if not remna_api_url:
await callback_query.message.edit_text(
"🚫 Нет доступного сервера Remnawave.",
reply_markup=build_editor_kb(tg_id),
)
return
async def _fetch_info_and_devices(api):
user_info = await api.get_user_by_uuid(client_id)
devices = await api.get_user_hwid_devices(client_id)
return user_info, devices
result = await with_remnawave_api(session, "", _fetch_info_and_devices, fallback_any=True, timeout_sec=8.0)
if result is None:
await callback_query.message.edit_text("❌ Ошибка авторизации в Remnawave.")
return
user_info, devices = result
status_emoji = "🟢"
status_text = "Онлайн"
online_at_str = "—"
first_connected_str = "—"
last_node_uuid = "—"
if not user_info:
status_emoji = "⚪️"
status_text = "Не найден"
else:
is_online = bool(user_info.get("isOnline"))
status_emoji = "🟢" if is_online else "⚪️"
status_text = "Онлайн" if is_online else "Офлайн"
online_at = user_info.get("onlineAt")
if online_at:
online_at_str = online_at[:19].replace("T", " ")
first_connected_at = user_info.get("firstConnectedAt")
if first_connected_at:
first_connected_str = first_connected_at[:19].replace("T", " ")
last_node_uuid_val = user_info.get("lastConnectedNodeUuid")
if last_node_uuid_val:
last_node_uuid = last_node_uuid_val
if not devices:
text = (
"💻 HWID устройства\n\n"
f"{status_emoji} Статус: {status_text}\n"
f"└ 🕓 Онлайн был: {online_at_str}\n"
f"└ 🚀 Первое подключение: {first_connected_str}\n"
f"└ 🛰 Нода последнего подключения: {last_node_uuid}\n\n"
"🔌 Нет привязанных устройств."
)
else:
text = (
"💻 HWID устройства\n\n"
f"{status_emoji} Статус: {status_text}\n"
f"└ 🕓 Онлайн был: {online_at_str}\n"
f"└ 🚀 Первое подключение: {first_connected_str}\n"
f"└ 🛰 Нода последнего подключения: {last_node_uuid}\n\n"
f"🔗 Привязано устройств: {len(devices)}\n\n"
)
for idx, device in enumerate(devices, 1):
created = device.get("createdAt", "")[:19].replace("T", " ")
updated = device.get("updatedAt", "")[:19].replace("T", " ")
text += (
f"{idx}. {device.get('hwid')}\n"
f"└ 📱 Модель: {device.get('deviceModel') or '—'}\n"
f"└ 🧠 Платформа: {device.get('platform') or '—'} / {device.get('osVersion') or '—'}\n"
f"└ 🌐 User-Agent: {device.get('userAgent') or '—'}\n"
f"└ 🕓 Создано: {created}\n"
f"└ 🔄 Обновлено: {updated}\n\n"
)
await callback_query.message.edit_text(text, reply_markup=build_hwid_menu_kb(key_ref, tg_id))
@router.callback_query(
AdminUserEditorCallback.filter(F.action == "users_hwid_reset"),
IsAdminFilter(),
)
async def handle_hwid_reset(
callback_query: CallbackQuery,
callback_data: AdminUserEditorCallback,
session: AsyncSession,
):
key_ref = str(callback_data.data)
tg_id = callback_data.tg_id
key_obj = await resolve_admin_key(session, tg_id, key_ref)
if not key_obj:
await callback_query.message.edit_text("🚫 Не удалось найти ключ.", reply_markup=build_editor_kb(tg_id))
return
client_id = key_obj.client_id
remna_api_url = await resolve_remnawave_api_url(session, "", fallback_any=True)
if not remna_api_url:
await callback_query.message.edit_text(
"🚫 Нет доступного сервера Remnawave.",
reply_markup=build_editor_kb(tg_id),
)
return
async def _reset_devices(api):
devices = await api.get_user_hwid_devices(client_id)
if not devices:
return 0, 0
deleted = 0
for device in devices:
if await api.delete_user_hwid_device(client_id, device["hwid"]):
deleted += 1
return len(devices), deleted
reset_result = await with_remnawave_api(session, "", _reset_devices, fallback_any=True, timeout_sec=12.0)
if reset_result is None:
await callback_query.message.edit_text("❌ Ошибка авторизации в Remnawave.")
return
total, deleted = reset_result
await invalidate_remnawave_profile(
session,
"",
str(client_id),
fallback_any=True,
)
if total == 0:
await callback_query.message.edit_text(
"ℹ️ У пользователя нет привязанных устройств.",
reply_markup=build_editor_kb(tg_id, True),
)
return
await callback_query.message.edit_text(
f"✅ Удалено HWID-устройств: {deleted} из {total}.",
reply_markup=build_editor_kb(tg_id, True),
)