Files
Solo_bot/filters/admin.py
T

102 lines
3.3 KiB
Python

import time
from aiogram.filters import BaseFilter
from aiogram.types import CallbackQuery, Message
from sqlalchemy import select
from config import ADMIN_ID
from database.db import async_session_maker
from database.models import Admin
from .permissions import normalize_permissions
_ADMIN_CACHE: dict[int, tuple[float, bool, bool, frozenset[str]]] = {}
_ADMIN_CACHE_TTL = 60
def _get_cached_admin(user_id: int) -> tuple[bool, bool, frozenset[str]] | None:
now = time.time()
entry = _ADMIN_CACHE.get(user_id)
if entry and entry[0] > now:
return entry[1], entry[2], entry[3]
return None
def _set_cached_admin(
user_id: int, is_admin: bool, is_superadmin: bool, permissions: frozenset[str]
) -> None:
_ADMIN_CACHE[user_id] = (time.time() + _ADMIN_CACHE_TTL, is_admin, is_superadmin, permissions)
def invalidate_admin_cache(user_id: int | None = None) -> None:
if user_id is None:
_ADMIN_CACHE.clear()
else:
_ADMIN_CACHE.pop(user_id, None)
async def _resolve_admin(user_id: int) -> tuple[bool, bool, frozenset[str]]:
cached = _get_cached_admin(user_id)
if cached is not None:
return cached
try:
async with async_session_maker() as session:
admin = (
await session.execute(select(Admin).where(Admin.tg_id == user_id))
).scalar_one_or_none()
admin_ids = (ADMIN_ID,) if isinstance(ADMIN_ID, int) else ADMIN_ID
is_admin = admin is not None or user_id in admin_ids
if admin:
is_super = admin.role != "moderator"
perms = frozenset(normalize_permissions(admin.permissions))
else:
is_super = user_id in admin_ids
perms = frozenset()
_set_cached_admin(user_id, is_admin, is_super, perms)
await session.commit()
return is_admin, is_super, perms
except Exception:
return False, False, frozenset()
class IsAdminFilter(BaseFilter):
async def __call__(self, event: Message | CallbackQuery) -> bool:
if not event.from_user:
return False
is_admin, _, _ = await _resolve_admin(event.from_user.id)
return is_admin
class IsSuperAdminFilter(BaseFilter):
async def __call__(self, event: Message | CallbackQuery) -> bool:
if not event.from_user:
return False
_, is_super, _ = await _resolve_admin(event.from_user.id)
return is_super
class HasPermission(BaseFilter):
def __init__(self, *permissions: str, require_all: bool = False) -> None:
if not permissions:
raise ValueError("HasPermission requires at least one permission id")
self.permissions = tuple(permissions)
self.require_all = require_all
async def __call__(self, event: Message | CallbackQuery) -> bool:
if not event.from_user:
return False
is_admin, is_super, perms = await _resolve_admin(event.from_user.id)
if not is_admin:
return False
if is_super:
return True
if self.require_all:
return all(p in perms for p in self.permissions)
return any(p in perms for p in self.permissions)
async def get_admin_context(user_id: int) -> tuple[bool, bool, frozenset[str]]:
return await _resolve_admin(user_id)