feat(bot): implement HWID device list and deletion

This commit is contained in:
2026-06-17 15:40:35 +07:00
parent edfe1e6874
commit 93f05e46b5
7 changed files with 123 additions and 20 deletions

View File

@@ -4,21 +4,31 @@ from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message
from sqlalchemy.ext.asyncio import AsyncSession
from bot.keyboards.client import close_kb, construct_devices_list, main_menu, subscription_controls
from bot.keyboards.client import (
close_kb,
construct_devices_list,
main_menu,
prompt_hwid_deletion_kb,
subscription_controls,
)
from bot.keyboards.common import return_to_menu
from bot.texts import (
CALLBACK_FALLBACK,
FAQ_TEXT,
GENERAL_PROCESSING,
HWID_LIST,
NO_CONNECTIONS,
NO_SUBSCRIPTION,
PROMPT_HWID_DELETION,
STANDARD_FALLBACK,
SUCCESSFUL_HWID_DELETION,
)
from db.models.users import User
from misc import utils
from misc.utils import build_main_menu_text, format_subscription_link
from schemas.di import DependenciesDTO
from schemas.subscriptions import InternalSquads, SubscriptionPlan
from services.rw import get_hwid_list, get_user_by_telegram_id
from services.rw import delete_hwid, get_hwid_list, get_user_by_telegram_id
router = Router()
@@ -159,6 +169,7 @@ async def sub_info(
),
)
@router.callback_query(F.data == "hwid_control")
async def hwid_control(cb: CallbackQuery, state: FSMContext, deps: DependenciesDTO):
await state.clear()
@@ -169,14 +180,46 @@ async def hwid_control(cb: CallbackQuery, state: FSMContext, deps: DependenciesD
if not rw_user:
await cb.message.edit_text(NO_SUBSCRIPTION, reply_markup=return_to_menu)
return
devices = await get_hwid_list(deps.rw_sdk, rw_user.uuid)
if not devices:
await cb.message.edit_text(NO_CONNECTIONS, reply_markup=return_to_menu)
return
await cb.message.edit_text(text="pepe", reply_markup=construct_devices_list(devices))
hwid_limit = max(rw_user.hwid_device_limit, 3)
await cb.message.edit_text(
text=HWID_LIST.format(count=len(devices), limit=hwid_limit),
reply_markup=construct_devices_list(devices),
)
@router.callback_query(F.data.startswith("hwid:"))
async def prompt_delete_hwid(cb: CallbackQuery, state: FSMContext):
await state.clear()
hwid = cb.data.split(":")[1]
await cb.message.edit_text(PROMPT_HWID_DELETION, reply_markup=prompt_hwid_deletion_kb(hwid))
@router.callback_query(F.data.startswith("delete_hwid:"))
async def delete_hwid_bot(cb: CallbackQuery, state: FSMContext, deps: DependenciesDTO):
await state.clear()
hwid = cb.data.split(":")[1]
await cb.message.edit_text(GENERAL_PROCESSING, reply_markup=close_kb)
rw_user = await get_user_by_telegram_id(deps.rw_sdk, cb.from_user.id)
if not rw_user:
await cb.message.edit_text(STANDARD_FALLBACK, reply_markup=return_to_menu)
return
status = await delete_hwid(deps.rw_sdk, rw_user.uuid, hwid)
if status:
await cb.message.edit_text(SUCCESSFUL_HWID_DELETION, reply_markup=return_to_menu)
else:
await cb.message.edit_text(STANDARD_FALLBACK, reply_markup=return_to_menu)
@router.callback_query(F.data == "toggle:autorenewal")

View File

@@ -3,7 +3,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from remnawave.models.hwid import HwidDeviceDto
from config import settings
from misc.utils import convert_duration_to_int
from misc.utils import convert_duration_to_int, format_hwid_device
from schemas.subscriptions import SubscriptionDuration
from .common import _return_to_menu_btn
@@ -146,10 +146,24 @@ def pay_url(url: str) -> InlineKeyboardMarkup:
]
).as_markup()
def construct_devices_list(devices: list[HwidDeviceDto]) -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder()
for device in devices:
builder.row(InlineKeyboardButton(text=f"{device.platform} | {device.device_model} | {device.user_agent.split('/')[0]}", callback_data=f"hwid:{device.hwid}"))
builder.row(
InlineKeyboardButton(
text=format_hwid_device(device), callback_data=f"hwid:{device.hwid}"
)
)
builder.row(_return_btn(data="sub_info"))
return builder.as_markup()
return builder.as_markup()
def prompt_hwid_deletion_kb(hwid: str) -> InlineKeyboardMarkup:
return InlineKeyboardBuilder(
[
[InlineKeyboardButton(text="🗑️ Удалить", callback_data=f"delete_hwid:{hwid}")],
[_return_btn("hwid_control", text="❌ Отмена")],
]
).as_markup()

View File

@@ -74,6 +74,13 @@ NO_SQUADS = "отсутствуют"
# — HWID лимит
UNLIMITED_HWID = "без лимитов"
# — OS эмодзи
WINDOWS = "💻"
LINUX = "🐧"
ANDROID = "💚"
APPLE = "🍎"
UNKNOWN_OS = "👤"
# — Трафик
NO_TRAFFIC_LIMIT = "не ограничен"
TRAFFIC_WITH_LIMIT = "{used} / {limit}"
@@ -155,7 +162,10 @@ SUBSCRIPTION_INFORMATION = (
NO_SUBSCRIPTION = f"{MALENIA_WARN} <b>Подписка не найдена.</b>"
NO_CONNECTIONS = f"{MALENIA_SHIELD} <b>Вы ещё не подключались по своей ссылке.</b>"
NO_CONNECTIONS = f"{MALENIA_SHIELD} <b>У вас нет подключённых устройств.</b>"
HWID_LIST = f"{MALENIA_SHIELD} <b>Ваши устройства ({{count}} / {{limit}}):</b>"
PROMPT_HWID_DELETION = f"{MALENIA_WARN} <b>Вы уверены что хотите удалить это устройство?</b>"
SUCCESSFUL_HWID_DELETION = f"{MALENIA_HEART} <b>Устройство успешно удалено.</b>"
# ============================================================
# РЕФЕРАЛЬНАЯ СИСТЕМА