Files
vpn-control-panel/app/bot/handlers/users.py
smolkik-code 7c7c88621d Initial commit: VPN Control Panel
- FastAPI REST API with JWT auth
- aiogram 3 Telegram bot with admin middleware
- APScheduler daily tasks (expiry, reminders, revoke, sync)
- SQLAlchemy 2 async ORM with Alembic migrations
- Jinja2 admin panel (Dashboard, Users, Payments, Servers, Tariffs)
- VPN provider abstraction with MockProvider
- Stats service with revenue/subscription analytics
- Docker Compose (PostgreSQL + Redis + app)
- Healthcheck endpoint
2026-07-05 17:50:11 +07:00

118 lines
3.6 KiB
Python

from datetime import datetime
from aiogram import Router
from aiogram.filters import Command, CommandObject
from aiogram.types import Message
from loguru import logger
from app.bot.texts import (
user_info,
user_short,
USER_NOT_FOUND,
INVALID_ARGS,
NO_DATA,
)
from app.database import async_session_factory
from app.repositories.user import UserRepository
from app.repositories.payment import PaymentRepository
from app.repositories.tariff import TariffRepository
from app.services.billing import BillingService
from app.services.user import UserService
router = Router(name="users")
@router.message(Command("users"))
async def cmd_users(message: Message) -> None:
async with async_session_factory() as session:
user_service = UserService(UserRepository(session))
users = await user_service.user_repo.get_active_users()
if not users:
await message.answer(NO_DATA)
return
lines = [f"👥 <b>Active users ({len(users)}):</b>", ""]
for u in users:
lines.append(user_short(u.id, u.telegram_id, u.username))
await message.answer("\n".join(lines), parse_mode="HTML")
@router.message(Command("user"))
async def cmd_user(message: Message, command: CommandObject) -> None:
if not command.args or not command.args.strip().isdigit():
await message.answer(
INVALID_ARGS.format("/user <telegram_id>"),
parse_mode="HTML",
)
return
telegram_id = int(command.args.strip())
async with async_session_factory() as session:
user_repo = UserRepository(session)
user = await user_repo.get_by_telegram_id(telegram_id)
if user is None:
await message.answer(
USER_NOT_FOUND.format(telegram_id), parse_mode="HTML"
)
return
billing = BillingService(
PaymentRepository(session),
TariffRepository(session),
user_repo,
)
sub = await billing.get_active_subscription(user.id)
await message.answer(
user_info(
user_id=user.id,
telegram_id=user.telegram_id,
username=user.username,
full_name=user.full_name,
is_active=user.is_active,
created_at=user.created_at.strftime("%Y-%m-%d"),
subscription_active=sub is not None and sub.is_active,
tariff_name=sub.tariff.name if sub and sub.tariff else None,
remaining_days=sub.remaining_days if sub else 0,
),
parse_mode="HTML",
)
@router.message(Command("delete"))
async def cmd_delete(message: Message, command: CommandObject) -> None:
if not command.args or not command.args.strip().isdigit():
await message.answer(
INVALID_ARGS.format("/delete <telegram_id>"),
parse_mode="HTML",
)
return
telegram_id = int(command.args.strip())
async with async_session_factory() as session:
user_repo = UserRepository(session)
user = await user_repo.get_by_telegram_id(telegram_id)
if user is None:
await message.answer(
USER_NOT_FOUND.format(telegram_id), parse_mode="HTML"
)
return
user_service = UserService(user_repo)
ok = await user_service.deactivate(user.id)
if ok:
await message.answer(
f"✅ User <code>{telegram_id}</code> deactivated.",
parse_mode="HTML",
)
logger.info("Bot: user deactivated tg={}", telegram_id)
else:
await message.answer("❌ Failed to deactivate user.")