Подготовка к релизу

This commit is contained in:
2025-10-30 18:28:26 +03:00
parent 69c0162c6b
commit 4981db90a0
191 changed files with 45557 additions and 3651 deletions
+434
View File
@@ -0,0 +1,434 @@
"""
Сервис модерации пользователей.
Отвечает за бизнес-логику модерации и администрирования.
"""
from typing import Dict, List, Optional, Tuple, Any
from dataclasses import dataclass
from datetime import datetime, timedelta
from core.exceptions import ValidationError, PermissionError
from utils.validators import InputValidator
import re
@dataclass
class ModerationAction:
"""Действие модерации"""
action_id: str
user_id: int
admin_id: int
action_type: str
reason: str
duration: Optional[int] = None # в секундах для временных наказаний
created_at: datetime = None
expires_at: Optional[datetime] = None
def __post_init__(self):
if self.created_at is None:
self.created_at = datetime.now()
if self.duration and self.expires_at is None:
self.expires_at = self.created_at + timedelta(seconds=self.duration)
class ModerationService:
"""
Сервис для модерации пользователей и администрирования.
Отвечает за:
- Управление предупреждениями
- Блокировку и разблокировку пользователей
- Модерацию контента
- Проверку прав администраторов
- Логирование действий модерации
"""
def __init__(self, user_repository, score_repository):
"""
Инициализация сервиса.
Args:
user_repository: Репозиторий пользователей
score_repository: Репозиторий очков и рейтингов
"""
self.user_repo = user_repository
self.score_repo = score_repository
# Настройки модерации
self.moderation_settings = {
'max_warnings': 3,
'ban_threshold': 5,
'mute_duration': 300, # 5 минут по умолчанию
'profanity_filter_enabled': True,
'auto_moderate_media': False
}
# Список запрещенных слов
self.profanity_words = [
'блядь', 'бля', 'блять', 'хуй', 'хуйня', 'пизда', 'пиздец',
'ебать', 'ебаный', 'сука', 'сукин', 'мудак', 'говно',
'fuck', 'shit', 'damn', 'asshole', 'bastard', 'cunt', 'dick', 'bitch'
]
# Активные модерационные действия
self.active_actions = {}
async def warn_user(self, user_id: int, reason: str, admin_id: int) -> Dict[str, Any]:
"""
Выдача предупреждения пользователю.
Args:
user_id: ID пользователя
reason: Причина предупреждения
admin_id: ID администратора
Returns:
Результат действия
"""
if not InputValidator.validate_text_content(reason, max_length=500):
raise ValidationError("Неверная причина предупреждения")
# Добавляем предупреждение в базу данных
print(f"[DEBUG] ModerationService.warn_user: calling user_repo.add_warning({user_id}, '{reason}', {admin_id})")
success = self.user_repo.add_warning(user_id, reason, admin_id)
if not success:
raise ValidationError("Не удалось выдать предупреждение")
# Получаем обновленное количество предупреждений
print(f"[DEBUG] ModerationService.warn_user: calling user_repo.get_user_warnings({user_id})")
current_warnings = self.user_repo.get_user_warnings(user_id)
# Проверяем необходимость автоматических действий
action_taken = None
if current_warnings >= self.moderation_settings['ban_threshold']:
action_taken = 'auto_ban'
await self.ban_user(user_id, "Автоматический бан за превышение лимита предупреждений", admin_id, permanent=True)
elif current_warnings >= self.moderation_settings['max_warnings']:
action_taken = 'auto_mute'
await self.mute_user(user_id, "Автоматическая заглушка за превышение лимита предупреждений", admin_id)
return {
'success': True,
'warnings_count': current_warnings,
'action_taken': action_taken,
'reason': reason
}
async def mute_user(self, user_id: int, reason: str, admin_id: int, duration: int = None) -> Dict[str, Any]:
"""
Заглушка пользователя.
Args:
user_id: ID пользователя
reason: Причина заглушки
admin_id: ID администратора
duration: Длительность в секундах
Returns:
Результат действия
"""
if duration is None:
duration = self.moderation_settings['mute_duration']
if not InputValidator.validate_number(duration, min_value=1, max_value=86400): # макс 24 часа
raise ValidationError("Неверная длительность заглушки")
# Создаем действие модерации
action_id = f"mute_{user_id}_{admin_id}_{datetime.now().strftime('%H%M%S')}"
action = ModerationAction(
action_id=action_id,
user_id=user_id,
admin_id=admin_id,
action_type='mute',
reason=reason,
duration=duration
)
self.active_actions[action_id] = action
# Логируем действие в базу данных
await self._log_moderation_action(user_id, admin_id, 'mute', reason, duration)
return {
'success': True,
'action_id': action_id,
'duration': duration,
'expires_at': action.expires_at
}
async def ban_user(self, user_id: int, reason: str, admin_id: int, permanent: bool = False) -> Dict[str, Any]:
"""
Блокировка пользователя.
Args:
user_id: ID пользователя
reason: Причина блокировки
admin_id: ID администратора
permanent: Постоянная блокировка
Returns:
Результат действия
"""
duration = None if permanent else (24 * 3600) # 24 часа по умолчанию
# Создаем действие модерации
action_id = f"ban_{user_id}_{admin_id}_{datetime.now().strftime('%H%M%S')}"
action = ModerationAction(
action_id=action_id,
user_id=user_id,
admin_id=admin_id,
action_type='ban',
reason=reason,
duration=duration
)
self.active_actions[action_id] = action
# Логируем действие в базу данных
await self._log_moderation_action(user_id, admin_id, 'ban', reason, duration)
return {
'success': True,
'action_id': action_id,
'permanent': permanent,
'duration': duration,
'expires_at': action.expires_at if not permanent else None
}
async def unban_user(self, user_id: int, admin_id: int) -> bool:
"""
Разблокировка пользователя.
Args:
user_id: ID пользователя
admin_id: ID администратора
Returns:
True если разблокировка успешна
"""
# Удаляем все активные действия бана для пользователя
to_remove = []
for action_id, action in self.active_actions.items():
if action.user_id == user_id and action.action_type == 'ban':
to_remove.append(action_id)
for action_id in to_remove:
del self.active_actions[action_id]
# Логируем действие в базу данных
await self._log_moderation_action(user_id, admin_id, 'unban', "Разблокировка пользователя")
return len(to_remove) > 0
async def unmute_user(self, user_id: int, admin_id: int) -> bool:
"""
Снятие заглушки с пользователя.
Args:
user_id: ID пользователя
admin_id: ID администратора
Returns:
True если снятие заглушки успешно
"""
# Удаляем все активные действия mute для пользователя
to_remove = []
for action_id, action in self.active_actions.items():
if action.user_id == user_id and action.action_type == 'mute':
to_remove.append(action_id)
for action_id in to_remove:
del self.active_actions[action_id]
# Логируем действие в базу данных
await self._log_moderation_action(user_id, admin_id, 'unmute', "Снятие заглушки")
return len(to_remove) > 0
def check_profanity(self, text: str) -> List[str]:
"""
Проверка текста на нецензурную лексику.
Args:
text: Проверяемый текст
Returns:
Список найденных запрещенных слов
"""
if not self.moderation_settings['profanity_filter_enabled']:
return []
found_words = []
text_lower = text.lower()
for word in self.profanity_words:
if word in text_lower:
found_words.append(word)
return found_words
async def moderate_message(self, user_id: int, text: str, chat_id: int) -> Dict[str, Any]:
"""
Модерация сообщения пользователя.
Args:
user_id: ID пользователя
text: Текст сообщения
chat_id: ID чата
Returns:
Результат модерации
"""
# Проверяем на нецензурную лексику
profanity_words = self.check_profanity(text)
if profanity_words:
# Автоматически выдаем предупреждение за нецензурную лексику
admin_id = 0 # Системный администратор для авто-модерации
await self.warn_user(user_id, f"Нецензурная лексика: {', '.join(profanity_words)}", admin_id)
return {
'action': 'warning',
'reason': 'Нецензурная лексика',
'profanity_words': profanity_words
}
return {
'action': 'none',
'clean': True
}
def is_user_muted(self, user_id: int) -> bool:
"""
Проверка, заглушен ли пользователь.
Args:
user_id: ID пользователя
Returns:
True если пользователь заглушен
"""
current_time = datetime.now()
for action in self.active_actions.values():
if (action.user_id == user_id and
action.action_type == 'mute' and
action.expires_at and
current_time < action.expires_at):
return True
return False
def is_user_banned(self, user_id: int) -> bool:
"""
Проверка, забанен ли пользователь.
Args:
user_id: ID пользователя
Returns:
True если пользователь забанен
"""
current_time = datetime.now()
for action in self.active_actions.values():
if (action.user_id == user_id and
action.action_type == 'ban' and
(action.expires_at is None or current_time < action.expires_at)):
return True
return False
async def get_moderation_stats(self) -> Dict[str, Any]:
"""
Получение статистики модерации.
Returns:
Статистика модерации
"""
# Подсчитываем активные действия
mutes = sum(1 for action in self.active_actions.values() if action.action_type == 'mute')
bans = sum(1 for action in self.active_actions.values() if action.action_type == 'ban')
return {
'active_mutes': mutes,
'active_bans': bans,
'total_actions': len(self.active_actions),
'profanity_filter_enabled': self.moderation_settings['profanity_filter_enabled']
}
async def get_user_moderation_history(self, user_id: int, limit: int = 10) -> List[Dict[str, Any]]:
"""
Получение истории модерации пользователя.
Args:
user_id: ID пользователя
limit: Максимальное количество записей
Returns:
История модерации
"""
# Здесь будет получение истории из базы данных
return []
async def _log_moderation_action(self, user_id: int, admin_id: int, action_type: str,
reason: str, duration: int = None):
"""
Логирование действия модерации в базу данных.
Args:
user_id: ID пользователя
admin_id: ID администратора
action_type: Тип действия
reason: Причина
duration: Длительность (для временных действий)
"""
# Здесь будет сохранение в базу данных через репозиторий
pass
def cleanup_expired_actions(self):
"""Очистка истекших модерационных действий"""
current_time = datetime.now()
to_remove = []
for action_id, action in self.active_actions.items():
if (action.expires_at and current_time > action.expires_at):
to_remove.append(action_id)
for action_id in to_remove:
del self.active_actions[action_id]
def add_profanity_word(self, word: str) -> bool:
"""
Добавление слова в фильтр нецензурной лексики.
Args:
word: Слово для добавления
Returns:
True если слово добавлено
"""
if not InputValidator.validate_text_content(word, max_length=50):
return False
if word.lower() not in self.profanity_words:
self.profanity_words.append(word.lower())
return True
return False
def remove_profanity_word(self, word: str) -> bool:
"""
Удаление слова из фильтра нецензурной лексики.
Args:
word: Слово для удаления
Returns:
True если слово удалено
"""
if word.lower() in self.profanity_words:
self.profanity_words.remove(word.lower())
return True
return False