This commit is contained in:
2026-02-20 03:12:47 +07:00
parent 5d350d0885
commit 5aca4e8438
23 changed files with 2291 additions and 1330 deletions

View File

@@ -68,7 +68,6 @@ class UserSpamStats:
"""Удаляет старые запросы за пределами временного окна"""
cutoff_time = current_time - time_window
# Удаляем старые запросы
new_times = []
new_contexts = []
@@ -121,7 +120,6 @@ class UserSpamStats:
current_time = time()
# 1. КРИТИЧНО: Экстремально быстрая отправка (флуд-бот)
# Если 5+ сообщений за 2 секунды => мгновенный мут
very_recent = [ctx for ctx in recent_contexts if (current_time - ctx.timestamp) < 2.0]
if len(very_recent) >= 5:
return {
@@ -130,7 +128,7 @@ class UserSpamStats:
'severity': 1.0,
'details': f"⚡ Экстремальный флуд: {len(very_recent)} сообщений за 2 секунды",
'instant_block': True,
'block_duration': 600.0 # 10 минут сразу
'block_duration': 600.0
}
# 2. КРИТИЧНО: 8+ сообщений за 5 секунд => агрессивный флуд
@@ -142,13 +140,12 @@ class UserSpamStats:
'severity': 0.95,
'details': f"🔥 Агрессивный флуд: {len(recent_5s)} сообщений за 5 секунд",
'instant_block': True,
'block_duration': 300.0 # 5 минут
'block_duration': 300.0
}
# 3. Медиа-флуд (стикеры/фото/видео)
# 3. Медиа-флуд
media_contexts = [ctx for ctx in recent_contexts if ctx.media_type]
if len(media_contexts) >= 7:
# Проверяем скорость отправки медиа
media_recent = [ctx for ctx in media_contexts if (current_time - ctx.timestamp) < 5.0]
if len(media_recent) >= 6:
return {
@@ -157,7 +154,7 @@ class UserSpamStats:
'severity': 0.9,
'details': f"📸 Медиа-флуд: {len(media_recent)} файлов за 5 секунд",
'instant_block': True,
'block_duration': 240.0 # 4 минуты
'block_duration': 240.0
}
return {
@@ -173,14 +170,14 @@ class UserSpamStats:
text_counts = Counter(texts)
most_common_text, count = text_counts.most_common(1)[0]
if count >= 5: # 5 одинаковых сообщений
if count >= 5:
return {
'is_spam': True,
'reason': 'identical_messages',
'severity': 0.85,
'details': f"📋 Повтор: '{most_common_text[:40]}...' ({count}x)",
'instant_block': True,
'block_duration': 180.0 # 3 минуты
'block_duration': 180.0
}
# 5. Проверка спама callback кнопок
@@ -189,14 +186,14 @@ class UserSpamStats:
callback_counts = Counter(callbacks)
most_common_callback, count = callback_counts.most_common(1)[0]
if count >= 10: # 10 нажатий одной кнопки
if count >= 10:
return {
'is_spam': True,
'reason': 'callback_spam',
'severity': 0.8,
'details': f"🔘 Спам кнопки: {count} нажатий",
'instant_block': True,
'block_duration': 120.0 # 2 минуты
'block_duration': 120.0
}
return {'is_spam': False, 'reason': None, 'severity': 0.0}
@@ -269,11 +266,11 @@ class AntiSpamMiddleware(BaseMiddleware):
- Детекция скорости отправки сообщений
- Адаптивная длительность блокировки
- Различает типы активности
- Бот никогда не банит сам себя
"""
def __init__(
self,
# Базовые лимиты (мягкие, для накопления варнингов)
rate_limit_text: int = 8,
rate_limit_forward: int = 20,
rate_limit_callback: int = 12,
@@ -281,12 +278,10 @@ class AntiSpamMiddleware(BaseMiddleware):
time_window: float = 10.0,
# Предупреждения (уже не так важны — флуд блокируется мгновенно)
warning_limit: int = 3,
base_block_duration: float = 120.0, # 2 минуты за накопленные варнинги
base_block_duration: float = 120.0,
max_block_duration: float = 3600.0,
# Опции
whitelist_admins: bool = True,
progressive_blocking: bool = True,
enable_smart_detection: bool = True,
@@ -318,7 +313,6 @@ class AntiSpamMiddleware(BaseMiddleware):
context.is_reply = event.reply_to_message is not None
context.is_command = bool(context.text and context.text.startswith('/'))
# Определяем тип медиа
if event.photo:
context.media_type = 'photo'
elif event.video:
@@ -350,7 +344,6 @@ class AntiSpamMiddleware(BaseMiddleware):
else:
base_limit = self.rate_limit_text
# Применяем репутацию
if self.enable_reputation:
base_limit = int(base_limit * user_stats.reputation)
@@ -392,6 +385,11 @@ class AntiSpamMiddleware(BaseMiddleware):
if user_id is None:
return await handler(event, data)
# ✅ ИСПРАВЛЕНИЕ: пропускаем самого бота (предотвращает самобан)
bot = data.get("bot")
if bot and user_id == bot.id:
return await handler(event, data)
user_str = f"@{event.from_user.username}" if event.from_user.username else f"id{user_id}"
# Whitelist для администраторов
@@ -414,7 +412,7 @@ class AntiSpamMiddleware(BaseMiddleware):
user=user_str
)
# НЕ отправляем сообщение каждый раз — только callback answer
# Только для callback — отвечаем алертом, для сообщений молчим
if isinstance(event, CallbackQuery):
await event.answer(
f"🚫 Блокировка: {self._format_duration(remaining)}",
@@ -426,10 +424,10 @@ class AntiSpamMiddleware(BaseMiddleware):
# Извлекаем контекст сообщения
context = self._extract_context(event)
# Добавляем запрос СНАЧАЛА (важно для детекции скорости)
# Добавляем запрос СНАЧАЛА важно для детекции скорости флуда
user_stats.add_request(current_time, context)
# Очищаем старые запросы
# Очищаем старые запросы за пределами временного окна
user_stats.clean_old_requests(current_time, self.time_window)
# ========== КРИТИЧНО: МГНОВЕННАЯ ДЕТЕКЦИЯ ФЛУДА ==========
@@ -437,10 +435,9 @@ class AntiSpamMiddleware(BaseMiddleware):
spam_analysis = user_stats.detect_spam_patterns(self.time_window)
if spam_analysis.get('is_spam') and spam_analysis.get('instant_block'):
# МГНОВЕННАЯ БЛОКИРОВКА
block_duration = spam_analysis.get('block_duration', 300.0)
user_stats.block(current_time, block_duration)
user_stats.warnings = self.warning_limit # Максимум варнингов
user_stats.warnings = self.warning_limit
spam_stats.instant_blocks += 1
logger.error(
@@ -461,7 +458,7 @@ class AntiSpamMiddleware(BaseMiddleware):
if isinstance(event, Message):
try:
await event.answer(block_message, parse_mode="HTML")
except:
except Exception:
pass
elif isinstance(event, CallbackQuery):
await event.answer(
@@ -471,10 +468,9 @@ class AntiSpamMiddleware(BaseMiddleware):
return None
# ========== ОБЫЧНАЯ ПРОВЕРКА ЛИМИТОВ (для мягких превышений) ==========
# ========== ОБЫЧНАЯ ПРОВЕРКА ЛИМИТОВ ==========
effective_limit = self._get_effective_rate_limit(user_stats, context)
# Подсчитываем релевантные запросы
relevant_requests = 0
for req_context in user_stats.message_contexts:
if context.is_forward and req_context.is_forward:
@@ -493,7 +489,6 @@ class AntiSpamMiddleware(BaseMiddleware):
user=user_str
)
# Мягкое превышение лимита
if relevant_requests >= effective_limit:
user_stats.add_warning()
spam_stats.total_warnings_issued += 1
@@ -505,7 +500,6 @@ class AntiSpamMiddleware(BaseMiddleware):
user=user_str
)
# Блокировка при достижении лимита варнингов
if user_stats.warnings >= self.warning_limit:
block_duration = self._calculate_block_duration(user_stats.warnings)
user_stats.block(current_time, block_duration)
@@ -526,7 +520,7 @@ class AntiSpamMiddleware(BaseMiddleware):
if isinstance(event, Message):
try:
await event.answer(block_message, parse_mode="HTML")
except:
except Exception:
pass
elif isinstance(event, CallbackQuery):
await event.answer(
@@ -536,7 +530,6 @@ class AntiSpamMiddleware(BaseMiddleware):
return None
# Предупреждение (только для сообщений, не для callback)
if isinstance(event, Message):
warning_message = (
f"⚠️ <b>Предупреждение {user_stats.warnings}/{self.warning_limit}</b>\n\n"
@@ -544,7 +537,7 @@ class AntiSpamMiddleware(BaseMiddleware):
)
try:
await event.answer(warning_message, parse_mode="HTML")
except:
except Exception:
pass
return None