218 lines
8.3 KiB
Python
218 lines
8.3 KiB
Python
import hashlib
|
|
|
|
from asyncio import sleep
|
|
|
|
from aiogram import F
|
|
from aiogram.fsm.context import FSMContext
|
|
from aiogram.fsm.state import State, StatesGroup
|
|
from aiogram.types import CallbackQuery, Message
|
|
from sqlalchemy import delete, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from database.models import Admin
|
|
from filters.admin import HasPermission, invalidate_admin_cache
|
|
from filters.permissions import (
|
|
ALL_PERMISSIONS,
|
|
PERMISSION_LABELS,
|
|
PERM_ADMINS,
|
|
normalize_permissions,
|
|
)
|
|
|
|
from . import router
|
|
from .keyboard import (
|
|
AdminPanelCallback,
|
|
build_admin_back_kb_to_admins,
|
|
build_admin_permissions_kb,
|
|
build_admins_kb,
|
|
build_role_selection_kb,
|
|
build_single_admin_menu,
|
|
build_token_result_kb,
|
|
)
|
|
|
|
|
|
class AdminState(StatesGroup):
|
|
waiting_for_tg_id = State()
|
|
|
|
|
|
@router.callback_query(AdminPanelCallback.filter(F.action == "admins"), HasPermission(PERM_ADMINS))
|
|
async def show_admins(callback: CallbackQuery, session: AsyncSession):
|
|
result = await session.execute(select(Admin.tg_id, Admin.role))
|
|
admins = result.all()
|
|
await callback.message.edit_text("👑 <b>Список админов</b>", reply_markup=build_admins_kb(admins))
|
|
|
|
|
|
@router.callback_query(AdminPanelCallback.filter(F.action == "add_admin"), HasPermission(PERM_ADMINS))
|
|
async def prompt_new_admin(callback: CallbackQuery, state: FSMContext):
|
|
await callback.message.edit_text(
|
|
"Введите <code>tg_id</code> нового админа:", reply_markup=build_admin_back_kb_to_admins()
|
|
)
|
|
await state.set_state(AdminState.waiting_for_tg_id)
|
|
|
|
|
|
@router.message(AdminState.waiting_for_tg_id, HasPermission(PERM_ADMINS))
|
|
async def save_new_admin(message: Message, session: AsyncSession, state: FSMContext):
|
|
try:
|
|
tg_id = int(message.text.strip())
|
|
except ValueError:
|
|
await message.answer("❌ Неверный формат. Введите числовой <code>tg_id</code>.")
|
|
return
|
|
|
|
result = await session.execute(select(Admin).where(Admin.tg_id == tg_id))
|
|
if result.scalar_one_or_none():
|
|
await message.answer("⚠️ Такой админ уже существует.")
|
|
else:
|
|
session.add(Admin(tg_id=tg_id, role="moderator", description="Добавлен вручную", permissions=[]))
|
|
invalidate_admin_cache(tg_id)
|
|
await message.answer(f"✅ Админ <code>{tg_id}</code> добавлен.", reply_markup=build_admin_back_kb_to_admins())
|
|
|
|
await state.clear()
|
|
|
|
|
|
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("admin_menu|")), HasPermission(PERM_ADMINS))
|
|
async def open_admin_menu(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession):
|
|
tg_id = int(callback_data.action.split("|")[1])
|
|
|
|
result = await session.execute(select(Admin.role).where(Admin.tg_id == tg_id))
|
|
admin = result.scalar_one_or_none()
|
|
role = admin or "moderator"
|
|
|
|
await callback.message.edit_text(
|
|
f"👤 <b>Управление админом</b> <code>{tg_id}</code>", reply_markup=build_single_admin_menu(tg_id, role)
|
|
)
|
|
|
|
|
|
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("generate_token|")), HasPermission(PERM_ADMINS))
|
|
async def generate_token(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession):
|
|
tg_id = int(callback_data.action.split("|")[1])
|
|
|
|
result = await session.execute(select(Admin).where(Admin.tg_id == tg_id))
|
|
admin = result.scalar_one_or_none()
|
|
if not admin:
|
|
await callback.message.edit_text("❌ Админ не найден.")
|
|
return
|
|
|
|
token = Admin.generate_token()
|
|
token_hash = hashlib.sha256(token.encode()).hexdigest()
|
|
admin.token = token_hash
|
|
|
|
msg = await callback.message.edit_text(
|
|
f"🎟 <b>Новый токен для</b> <code>{tg_id}</code>:\n\n"
|
|
f"<code>{token}</code>\n\n"
|
|
f"⚠️ Это сообщение исчезнет через 5 минут.",
|
|
reply_markup=build_token_result_kb(token),
|
|
)
|
|
|
|
await sleep(300)
|
|
try:
|
|
await msg.delete()
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("edit_role|")), HasPermission(PERM_ADMINS))
|
|
async def edit_admin_role(callback: CallbackQuery, callback_data: AdminPanelCallback):
|
|
tg_id = int(callback_data.action.split("|")[1])
|
|
await callback.message.edit_text(
|
|
f"✏ <b>Выберите новую роль для</b> <code>{tg_id}</code>:", reply_markup=build_role_selection_kb(tg_id)
|
|
)
|
|
|
|
|
|
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("set_role|")), HasPermission(PERM_ADMINS))
|
|
async def set_admin_role(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession):
|
|
try:
|
|
_, tg_id_str, role = callback_data.action.split("|")
|
|
tg_id = int(tg_id_str)
|
|
if role not in ("superadmin", "moderator"):
|
|
raise ValueError
|
|
except Exception:
|
|
await callback.message.edit_text("❌ Неверный формат.")
|
|
return
|
|
|
|
if tg_id == callback.from_user.id:
|
|
await callback.message.edit_text(
|
|
"🚫 <b>Нельзя изменить свою собственную роль!</b>", reply_markup=build_single_admin_menu(tg_id)
|
|
)
|
|
return
|
|
|
|
result = await session.execute(select(Admin).where(Admin.tg_id == tg_id))
|
|
admin = result.scalar_one_or_none()
|
|
if not admin:
|
|
await callback.message.edit_text("❌ Админ не найден.")
|
|
return
|
|
|
|
admin.role = role
|
|
invalidate_admin_cache(tg_id)
|
|
|
|
await callback.message.edit_text(
|
|
f"✅ Роль админа <code>{tg_id}</code> изменена на <b>{role}</b>.", reply_markup=build_single_admin_menu(tg_id, role)
|
|
)
|
|
|
|
|
|
@router.callback_query(
|
|
AdminPanelCallback.filter(F.action.startswith("edit_perms|")), HasPermission(PERM_ADMINS)
|
|
)
|
|
async def edit_admin_permissions(
|
|
callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession
|
|
):
|
|
tg_id = int(callback_data.action.split("|")[1])
|
|
|
|
admin = (await session.execute(select(Admin).where(Admin.tg_id == tg_id))).scalar_one_or_none()
|
|
if not admin:
|
|
await callback.message.edit_text("❌ Админ не найден.", reply_markup=build_admin_back_kb_to_admins())
|
|
return
|
|
|
|
current = set(normalize_permissions(admin.permissions))
|
|
await callback.message.edit_text(
|
|
f"🔐 <b>Права админа</b> <code>{tg_id}</code>\n\nНажмите чтобы переключить. "
|
|
f"Роль <b>superadmin</b> имеет все права автоматически.",
|
|
reply_markup=build_admin_permissions_kb(tg_id, current),
|
|
)
|
|
|
|
|
|
@router.callback_query(
|
|
AdminPanelCallback.filter(F.action.startswith("toggle_perm|")), HasPermission(PERM_ADMINS)
|
|
)
|
|
async def toggle_admin_permission(
|
|
callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession
|
|
):
|
|
try:
|
|
_, tg_id_str, perm_id = callback_data.action.split("|", 2)
|
|
tg_id = int(tg_id_str)
|
|
except ValueError:
|
|
await callback.answer("Неверный формат", show_alert=True)
|
|
return
|
|
|
|
if perm_id not in PERMISSION_LABELS:
|
|
await callback.answer("Неизвестное право", show_alert=True)
|
|
return
|
|
|
|
admin = (await session.execute(select(Admin).where(Admin.tg_id == tg_id))).scalar_one_or_none()
|
|
if not admin:
|
|
await callback.answer("Админ не найден", show_alert=True)
|
|
return
|
|
|
|
current = set(normalize_permissions(admin.permissions))
|
|
if perm_id in current:
|
|
current.discard(perm_id)
|
|
else:
|
|
current.add(perm_id)
|
|
admin.permissions = [p for p in ALL_PERMISSIONS if p in current]
|
|
invalidate_admin_cache(tg_id)
|
|
|
|
await callback.message.edit_reply_markup(
|
|
reply_markup=build_admin_permissions_kb(tg_id, current)
|
|
)
|
|
await callback.answer()
|
|
|
|
|
|
@router.callback_query(AdminPanelCallback.filter(F.action.startswith("delete_admin|")), HasPermission(PERM_ADMINS))
|
|
async def delete_admin(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession):
|
|
tg_id = int(callback_data.action.split("|")[1])
|
|
|
|
await session.execute(delete(Admin).where(Admin.tg_id == tg_id))
|
|
invalidate_admin_cache(tg_id)
|
|
|
|
await callback.message.edit_text(
|
|
f"🗑 Админ <code>{tg_id}</code> удалён.", reply_markup=build_admin_back_kb_to_admins()
|
|
)
|