mirror of
https://github.com/FerraSoft/bottohelp.git
synced 2026-08-06 21:55:03 +00:00
Подготовка к релизу
This commit is contained in:
@@ -0,0 +1,377 @@
|
||||
"""
|
||||
Система ролей и разрешений бота.
|
||||
Определяет уровни доступа и правила для различных операций.
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Set
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class UserRole(Enum):
|
||||
"""Перечисление ролей пользователей"""
|
||||
USER = "user" # Обычный пользователь
|
||||
MODERATOR = "moderator" # Модератор
|
||||
ADMIN = "admin" # Администратор
|
||||
SUPER_ADMIN = "super_admin" # Супер-администратор
|
||||
|
||||
|
||||
class Permission(Enum):
|
||||
"""Перечисление разрешений"""
|
||||
# Базовые команды (доступны всем)
|
||||
USE_BASIC_COMMANDS = "use_basic_commands"
|
||||
USE_GAMES = "use_games"
|
||||
USE_INFO = "use_info"
|
||||
USE_DONATE = "use_donate"
|
||||
|
||||
# Модераторские разрешения
|
||||
WARN_USERS = "warn_users"
|
||||
MUTE_USERS = "mute_users"
|
||||
VIEW_MODERATION_STATS = "view_moderation_stats"
|
||||
|
||||
# Администраторские разрешения
|
||||
BAN_USERS = "ban_users"
|
||||
KICK_USERS = "kick_users"
|
||||
MANAGE_SCHEDULED_POSTS = "manage_scheduled_posts"
|
||||
EXPORT_DATA = "export_data"
|
||||
VIEW_ADMIN_STATS = "view_admin_stats"
|
||||
|
||||
# Супер-администраторские разрешения
|
||||
MANAGE_SYSTEM = "manage_system"
|
||||
MANAGE_ADMINS = "manage_admins"
|
||||
VIEW_SYSTEM_LOGS = "view_system_logs"
|
||||
MANAGE_ERRORS = "manage_errors"
|
||||
|
||||
|
||||
@dataclass
|
||||
class RolePermissions:
|
||||
"""Разрешения для конкретной роли"""
|
||||
role: UserRole
|
||||
permissions: Set[Permission]
|
||||
description: str
|
||||
|
||||
|
||||
# Определение разрешений для каждой роли
|
||||
ROLE_PERMISSIONS = {
|
||||
UserRole.USER: RolePermissions(
|
||||
role=UserRole.USER,
|
||||
permissions={
|
||||
Permission.USE_BASIC_COMMANDS,
|
||||
Permission.USE_GAMES,
|
||||
Permission.USE_INFO,
|
||||
Permission.USE_DONATE,
|
||||
},
|
||||
description="Обычный пользователь с базовым доступом"
|
||||
),
|
||||
|
||||
UserRole.MODERATOR: RolePermissions(
|
||||
role=UserRole.MODERATOR,
|
||||
permissions={
|
||||
Permission.USE_BASIC_COMMANDS,
|
||||
Permission.USE_GAMES,
|
||||
Permission.USE_INFO,
|
||||
Permission.USE_DONATE,
|
||||
Permission.WARN_USERS,
|
||||
Permission.MUTE_USERS,
|
||||
Permission.VIEW_MODERATION_STATS,
|
||||
},
|
||||
description="Модератор с правами на предупреждения и заглушку"
|
||||
),
|
||||
|
||||
UserRole.ADMIN: RolePermissions(
|
||||
role=UserRole.ADMIN,
|
||||
permissions={
|
||||
Permission.USE_BASIC_COMMANDS,
|
||||
Permission.USE_GAMES,
|
||||
Permission.USE_INFO,
|
||||
Permission.USE_DONATE,
|
||||
Permission.WARN_USERS,
|
||||
Permission.MUTE_USERS,
|
||||
Permission.VIEW_MODERATION_STATS,
|
||||
Permission.BAN_USERS,
|
||||
Permission.KICK_USERS,
|
||||
Permission.MANAGE_SCHEDULED_POSTS,
|
||||
Permission.EXPORT_DATA,
|
||||
Permission.VIEW_ADMIN_STATS,
|
||||
},
|
||||
description="Администратор с полными правами модерации и управления"
|
||||
),
|
||||
|
||||
UserRole.SUPER_ADMIN: RolePermissions(
|
||||
role=UserRole.SUPER_ADMIN,
|
||||
permissions=set(Permission), # Все разрешения
|
||||
description="Супер-администратор с полным доступом к системе"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class PermissionManager:
|
||||
"""
|
||||
Менеджер разрешений для проверки прав доступа.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.role_permissions = ROLE_PERMISSIONS
|
||||
|
||||
def get_role_permissions(self, role: UserRole) -> Set[Permission]:
|
||||
"""
|
||||
Получение множества разрешений для роли.
|
||||
|
||||
Args:
|
||||
role: Роль пользователя
|
||||
|
||||
Returns:
|
||||
Множество разрешений
|
||||
"""
|
||||
if role in self.role_permissions:
|
||||
return self.role_permissions[role].permissions
|
||||
return set()
|
||||
|
||||
def has_permission(self, user_role: UserRole, permission: Permission) -> bool:
|
||||
"""
|
||||
Проверка наличия разрешения у роли.
|
||||
|
||||
Args:
|
||||
user_role: Роль пользователя
|
||||
permission: Проверяемое разрешение
|
||||
|
||||
Returns:
|
||||
True если разрешение есть
|
||||
"""
|
||||
role_permissions = self.get_role_permissions(user_role)
|
||||
return permission in role_permissions
|
||||
|
||||
def get_user_role_from_config(self, user_id: int, config) -> UserRole:
|
||||
"""
|
||||
Определение роли пользователя на основе конфигурации.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя
|
||||
config: Конфигурация приложения
|
||||
|
||||
Returns:
|
||||
Роль пользователя
|
||||
"""
|
||||
print(f"[DEBUG] get_user_role_from_config: user_id={user_id}")
|
||||
|
||||
# Проверяем супер-администраторов
|
||||
if hasattr(config, 'bot_config') and hasattr(config.bot_config, 'super_admin_ids'):
|
||||
super_admin_ids = config.bot_config.super_admin_ids
|
||||
print(f"[DEBUG] get_user_role_from_config: super_admin_ids={super_admin_ids}")
|
||||
if user_id in super_admin_ids:
|
||||
print(f"[DEBUG] get_user_role_from_config: user {user_id} is SUPER_ADMIN")
|
||||
return UserRole.SUPER_ADMIN
|
||||
|
||||
# Проверяем администраторов
|
||||
if hasattr(config, 'bot_config') and hasattr(config.bot_config, 'admin_ids'):
|
||||
admin_ids = config.bot_config.admin_ids
|
||||
print(f"[DEBUG] get_user_role_from_config: admin_ids={admin_ids}")
|
||||
if user_id in admin_ids:
|
||||
print(f"[DEBUG] get_user_role_from_config: user {user_id} is ADMIN")
|
||||
return UserRole.ADMIN
|
||||
|
||||
# Проверяем модераторов
|
||||
if hasattr(config, 'bot_config') and hasattr(config.bot_config, 'moderator_ids'):
|
||||
if user_id in config.bot_config.moderator_ids:
|
||||
return UserRole.MODERATOR
|
||||
|
||||
# По умолчанию - обычный пользователь
|
||||
print(f"[DEBUG] get_user_role_from_config: user {user_id} is USER (default)")
|
||||
return UserRole.USER
|
||||
|
||||
async def get_user_role_from_chat(self, update, user_id: int) -> UserRole:
|
||||
"""
|
||||
Определение роли пользователя на основе прав в чате.
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
user_id: ID пользователя
|
||||
|
||||
Returns:
|
||||
Роль пользователя
|
||||
"""
|
||||
try:
|
||||
chat = update.effective_chat
|
||||
if chat:
|
||||
member = await chat.get_member(user_id)
|
||||
if member.status == 'creator':
|
||||
return UserRole.SUPER_ADMIN
|
||||
elif member.status == 'administrator':
|
||||
return UserRole.ADMIN
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return UserRole.USER
|
||||
|
||||
async def get_effective_role(self, update, user_id: int, config) -> UserRole:
|
||||
"""
|
||||
Получение эффективной роли пользователя (максимальная из всех источников).
|
||||
|
||||
Args:
|
||||
update: Обновление от Telegram
|
||||
user_id: ID пользователя
|
||||
config: Конфигурация приложения
|
||||
|
||||
Returns:
|
||||
Эффективная роль пользователя
|
||||
"""
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Проверяем роли в порядке приоритета
|
||||
role_priority = {
|
||||
UserRole.SUPER_ADMIN: 4,
|
||||
UserRole.ADMIN: 3,
|
||||
UserRole.MODERATOR: 2,
|
||||
UserRole.USER: 1
|
||||
}
|
||||
|
||||
# Получаем роль из конфигурации
|
||||
config_role = UserRole.USER
|
||||
if config is not None:
|
||||
config_role = self.get_user_role_from_config(user_id, config)
|
||||
logger.debug(f"[DEBUG] get_effective_role: config_role for user {user_id} = {config_role} (config provided: {config is not None})")
|
||||
|
||||
# Получаем роль из базы данных (если есть доступ к репозиторию)
|
||||
db_role = await self._get_user_role_from_database(user_id)
|
||||
logger.debug(f"[DEBUG] get_effective_role: db_role for user {user_id} = {db_role}")
|
||||
|
||||
# Получаем роль из чата
|
||||
chat_role = UserRole.USER
|
||||
if update is not None:
|
||||
chat_role = await self.get_user_role_from_chat(update, user_id)
|
||||
logger.debug(f"[DEBUG] get_effective_role: chat_role for user {user_id} = {chat_role} (update provided: {update is not None})")
|
||||
|
||||
# Возвращаем роль с максимальным приоритетом
|
||||
roles = [config_role, db_role, chat_role]
|
||||
max_priority_role = max(roles, key=lambda r: role_priority[r])
|
||||
logger.info(f"[ROLE_DEBUG] User {user_id}: config={config_role}, db={db_role}, chat={chat_role} -> FINAL={max_priority_role}")
|
||||
|
||||
return max_priority_role
|
||||
|
||||
async def _get_user_role_from_database(self, user_id: int) -> UserRole:
|
||||
"""
|
||||
Получение роли пользователя из базы данных.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя
|
||||
|
||||
Returns:
|
||||
Роль пользователя из базы данных
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# Импортируем репозиторий для получения данных пользователя
|
||||
from database.repository import UserRepository
|
||||
|
||||
logger.debug(f"[DEBUG] _get_user_role_from_database: Checking role for user {user_id}")
|
||||
|
||||
# Получаем путь к базе данных из переменной окружения или по умолчанию
|
||||
db_path = os.getenv('DATABASE_URL', 'telegram_bot/telegram_bot.db')
|
||||
logger.debug(f"[DEBUG] _get_user_role_from_database: db_path = {db_path}")
|
||||
user_repo = UserRepository(db_path)
|
||||
user_data = await user_repo.get_by_id_async(user_id)
|
||||
|
||||
logger.debug(f"[DEBUG] _get_user_role_from_database: user_data = {user_data}")
|
||||
|
||||
if user_data and 'role_id' in user_data:
|
||||
role_id = user_data['role_id']
|
||||
logger.debug(f"[DEBUG] _get_user_role_from_database: role_id = {role_id}")
|
||||
|
||||
# Преобразование role_id в UserRole
|
||||
role_mapping = {
|
||||
1: UserRole.USER,
|
||||
2: UserRole.MODERATOR,
|
||||
3: UserRole.ADMIN,
|
||||
4: UserRole.SUPER_ADMIN
|
||||
}
|
||||
role = role_mapping.get(role_id, UserRole.USER)
|
||||
logger.debug(f"[DEBUG] _get_user_role_from_database: mapped to role = {role}")
|
||||
return role
|
||||
else:
|
||||
logger.debug(f"[DEBUG] _get_user_role_from_database: no role_id in user_data")
|
||||
|
||||
except Exception as e:
|
||||
# Если не удалось получить роль из базы данных, возвращаем USER
|
||||
logger.warning(f"[DEBUG] _get_user_role_from_database: exception {e} (returning USER role)")
|
||||
|
||||
return UserRole.USER
|
||||
|
||||
def get_required_permissions_for_command(self, command: str) -> Set[Permission]:
|
||||
"""
|
||||
Получение необходимых разрешений для команды.
|
||||
|
||||
Args:
|
||||
command: Название команды (без слеша)
|
||||
|
||||
Returns:
|
||||
Множество необходимых разрешений
|
||||
"""
|
||||
# Базовые команды доступны всем
|
||||
basic_commands = {
|
||||
'start', 'help', 'info', 'rank', 'leaderboard',
|
||||
'weather', 'news', 'translate', 'donate'
|
||||
}
|
||||
|
||||
if command in basic_commands:
|
||||
return {Permission.USE_BASIC_COMMANDS}
|
||||
|
||||
# Игровые команды
|
||||
game_commands = {'play_game', 'game_rps_start', 'game_2048_start'}
|
||||
if command in game_commands or command.startswith('game_'):
|
||||
return {Permission.USE_GAMES}
|
||||
|
||||
# Модераторские команды
|
||||
moderator_commands = {'warn', 'mute', 'unmute'}
|
||||
if command in moderator_commands:
|
||||
return {Permission.WARN_USERS, Permission.MUTE_USERS}
|
||||
|
||||
# Администраторские команды
|
||||
admin_commands = {
|
||||
'ban', 'unban', 'kick', 'admin_stats', 'export_stats',
|
||||
'schedule_post', 'list_posts', 'delete_post', 'publish_now',
|
||||
'menu_moderation', 'menu_admin', 'menu_triggers', 'admin_users'
|
||||
}
|
||||
if command in admin_commands:
|
||||
return {Permission.BAN_USERS, Permission.VIEW_ADMIN_STATS}
|
||||
|
||||
# Команды системы ошибок (доступны администраторам)
|
||||
error_commands = {
|
||||
'report_error', 'admin_errors', 'analyze_error_ai',
|
||||
'process_all_errors_ai', 'add_error_to_todo', 'add_all_analyzed_to_todo'
|
||||
}
|
||||
if command in error_commands:
|
||||
return {Permission.MANAGE_ERRORS}
|
||||
|
||||
# Меню команды (доступны всем)
|
||||
menu_commands = {
|
||||
'menu_main', 'menu_help', 'menu_rank', 'menu_donate', 'menu_leaderboard'
|
||||
}
|
||||
if command in menu_commands:
|
||||
return {Permission.USE_BASIC_COMMANDS}
|
||||
|
||||
# По умолчанию требуем базовые разрешения
|
||||
return {Permission.USE_BASIC_COMMANDS}
|
||||
|
||||
def can_execute_command(self, user_role: UserRole, command: str) -> bool:
|
||||
"""
|
||||
Проверка возможности выполнения команды пользователем.
|
||||
|
||||
Args:
|
||||
user_role: Роль пользователя
|
||||
command: Название команды
|
||||
|
||||
Returns:
|
||||
True если команда может быть выполнена
|
||||
"""
|
||||
required_permissions = self.get_required_permissions_for_command(command)
|
||||
user_permissions = self.get_role_permissions(user_role)
|
||||
|
||||
return required_permissions.issubset(user_permissions)
|
||||
|
||||
|
||||
# Глобальный экземпляр менеджера разрешений
|
||||
permission_manager = PermissionManager()
|
||||
Reference in New Issue
Block a user