forked from Muzifs/LGBot
Добавление медленного режима в зависимости от кармы
This commit is contained in:
@@ -196,6 +196,13 @@ COMMAND_MESSAGES = {
|
|||||||
"<b>🔥 БОНУС: Благодарность с восклицательным знаком даёт x2 кармы!</b>\n"
|
"<b>🔥 БОНУС: Благодарность с восклицательным знаком даёт x2 кармы!</b>\n"
|
||||||
"• спасибо! → +2 кармы 👍👍\n"
|
"• спасибо! → +2 кармы 👍👍\n"
|
||||||
"• thanks! → +2 кармы 👍👍\n\n"
|
"• thanks! → +2 кармы 👍👍\n\n"
|
||||||
|
"<b>⏱ Медленный режим на основе кармы:</b>\n"
|
||||||
|
"• Карма 0: 30 сек между сообщениями\n"
|
||||||
|
"• Карма > 0: меньше задержка (50+ = нет задержки)\n"
|
||||||
|
"• Карма < 0: больше задержка (-50 = 120 сек)\n\n"
|
||||||
|
"<b>⚠️ Снятие кармы:</b>\n"
|
||||||
|
"• Предупреждение (/warn): -5 кармы\n"
|
||||||
|
"• Мут (/mute или автомут): -10 кармы\n\n"
|
||||||
"<i>⏱ Одному пользователю можно давать карму раз в час</i>"
|
"<i>⏱ Одному пользователю можно давать карму раз в час</i>"
|
||||||
),
|
),
|
||||||
'top_karma_help': (
|
'top_karma_help': (
|
||||||
|
149
src/karma_slow_mode.py
Normal file
149
src/karma_slow_mode.py
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# Система медленного режима на основе кармы
|
||||||
|
# Управляет частотой отправки сообщений пользователями в зависимости от их кармы
|
||||||
|
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import Dict, Tuple
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Хранилище последних сообщений: {(user_id, chat_id): timestamp}
|
||||||
|
_last_message_times: Dict[Tuple[int, int], float] = {}
|
||||||
|
|
||||||
|
def calculate_slow_mode_delay(karma: int) -> int:
|
||||||
|
"""
|
||||||
|
Вычисляет задержку между сообщениями на основе кармы пользователя.
|
||||||
|
|
||||||
|
Логика:
|
||||||
|
- Карма = 0: 30 секунд
|
||||||
|
- Карма < 0: прогрессивное увеличение (до 120 секунд при карме -50 и ниже)
|
||||||
|
- Карма > 0: прогрессивное уменьшение (до 0 секунд при карме 50 и выше)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
karma: Количество кармы пользователя
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Задержка в секундах
|
||||||
|
"""
|
||||||
|
if karma == 0:
|
||||||
|
return 30
|
||||||
|
|
||||||
|
elif karma > 0:
|
||||||
|
# Положительная карма: уменьшаем задержку
|
||||||
|
# От 30 секунд (карма 1) до 0 секунд (карма 50+)
|
||||||
|
# Формула: 30 - (karma * 0.6)
|
||||||
|
delay = max(0, 30 - (karma * 0.6))
|
||||||
|
return int(delay)
|
||||||
|
|
||||||
|
else: # karma < 0
|
||||||
|
# Отрицательная карма: увеличиваем задержку
|
||||||
|
# От 30 секунд (карма -1) до 120 секунд (карма -50 и ниже)
|
||||||
|
# Формула: 30 + (abs(karma) * 1.8)
|
||||||
|
delay = min(120, 30 + (abs(karma) * 1.8))
|
||||||
|
return int(delay)
|
||||||
|
|
||||||
|
def check_slow_mode(user_id: int, chat_id: int, karma: int) -> Tuple[bool, int]:
|
||||||
|
"""
|
||||||
|
Проверяет, может ли пользователь отправить сообщение согласно медленному режиму.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: ID пользователя
|
||||||
|
chat_id: ID чата
|
||||||
|
karma: Карма пользователя
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[bool, int]: (можно ли отправить сообщение, оставшееся время ожидания в секундах)
|
||||||
|
"""
|
||||||
|
current_time = time.time()
|
||||||
|
key = (user_id, chat_id)
|
||||||
|
|
||||||
|
# Вычисляем необходимую задержку
|
||||||
|
required_delay = calculate_slow_mode_delay(karma)
|
||||||
|
|
||||||
|
# Если задержка 0, пользователь может писать без ограничений
|
||||||
|
if required_delay == 0:
|
||||||
|
_last_message_times[key] = current_time
|
||||||
|
return True, 0
|
||||||
|
|
||||||
|
# Проверяем, есть ли запись о последнем сообщении
|
||||||
|
if key not in _last_message_times:
|
||||||
|
_last_message_times[key] = current_time
|
||||||
|
return True, 0
|
||||||
|
|
||||||
|
# Вычисляем время с последнего сообщения
|
||||||
|
last_message_time = _last_message_times[key]
|
||||||
|
time_passed = current_time - last_message_time
|
||||||
|
|
||||||
|
# Проверяем, прошло ли достаточно времени
|
||||||
|
if time_passed >= required_delay:
|
||||||
|
_last_message_times[key] = current_time
|
||||||
|
return True, 0
|
||||||
|
else:
|
||||||
|
# Вычисляем оставшееся время ожидания
|
||||||
|
remaining_time = int(required_delay - time_passed)
|
||||||
|
return False, remaining_time
|
||||||
|
|
||||||
|
def get_slow_mode_info(karma: int) -> str:
|
||||||
|
"""
|
||||||
|
Возвращает информацию о медленном режиме для данного уровня кармы.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
karma: Карма пользователя
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Строка с описанием режима
|
||||||
|
"""
|
||||||
|
delay = calculate_slow_mode_delay(karma)
|
||||||
|
|
||||||
|
if delay == 0:
|
||||||
|
return "🟢 Медленный режим отключен (высокая карма!)"
|
||||||
|
elif delay <= 10:
|
||||||
|
return f"🟢 Медленный режим: {delay} сек (хорошая карма)"
|
||||||
|
elif delay <= 30:
|
||||||
|
return f"🟡 Медленный режим: {delay} сек (нейтральная карма)"
|
||||||
|
elif delay <= 60:
|
||||||
|
return f"🟠 Медленный режим: {delay} сек (низкая карма)"
|
||||||
|
else:
|
||||||
|
return f"🔴 Медленный режим: {delay} сек (очень низкая карма)"
|
||||||
|
|
||||||
|
def format_time(seconds: int) -> str:
|
||||||
|
"""
|
||||||
|
Форматирует время ожидания в читаемый формат.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seconds: Количество секунд
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Отформатированная строка
|
||||||
|
"""
|
||||||
|
if seconds < 60:
|
||||||
|
return f"{seconds} сек"
|
||||||
|
else:
|
||||||
|
minutes = seconds // 60
|
||||||
|
remaining_seconds = seconds % 60
|
||||||
|
if remaining_seconds > 0:
|
||||||
|
return f"{minutes} мин {remaining_seconds} сек"
|
||||||
|
else:
|
||||||
|
return f"{minutes} мин"
|
||||||
|
|
||||||
|
def cleanup_old_records(max_age_seconds: int = 3600):
|
||||||
|
"""
|
||||||
|
Очищает устаревшие записи из кэша (старше max_age_seconds).
|
||||||
|
Рекомендуется вызывать периодически для экономии памяти.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
max_age_seconds: Максимальный возраст записи в секундах (по умолчанию 1 час)
|
||||||
|
"""
|
||||||
|
current_time = time.time()
|
||||||
|
keys_to_remove = []
|
||||||
|
|
||||||
|
for key, timestamp in _last_message_times.items():
|
||||||
|
if current_time - timestamp > max_age_seconds:
|
||||||
|
keys_to_remove.append(key)
|
||||||
|
|
||||||
|
for key in keys_to_remove:
|
||||||
|
del _last_message_times[key]
|
||||||
|
|
||||||
|
if keys_to_remove:
|
||||||
|
logger.info(f"Очищено {len(keys_to_remove)} устаревших записей slow mode")
|
80
src/main.py
80
src/main.py
@@ -76,6 +76,22 @@ class UserUpdateMiddleware(BaseMiddleware):
|
|||||||
tag=message.from_user.username
|
tag=message.from_user.username
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ВАЖНО: Кэшируем сообщение для обработки реакций
|
||||||
|
# Импортируем функцию кэширования из karma_tracker
|
||||||
|
try:
|
||||||
|
from modules import karma_tracker
|
||||||
|
if hasattr(karma_tracker, '_cache_message') and message.chat.type in ['group', 'supergroup']:
|
||||||
|
karma_tracker._cache_message(message.chat.id, message.message_id, message.from_user.id)
|
||||||
|
logger.debug(f"[CACHE] Сообщение {message.message_id} от {message.from_user.id} добавлено в кэш")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка кэширования сообщения: {e}")
|
||||||
|
|
||||||
|
# ВАЖНО: Проверяем slow mode на основе кармы
|
||||||
|
# Применяется только в групповых чатах и не для команд
|
||||||
|
if await self._check_slow_mode(message):
|
||||||
|
# Сообщение заблокировано slow mode, прерываем обработку
|
||||||
|
return None
|
||||||
|
|
||||||
# ВАЖНО: Проверяем на мат ДО передачи другим обработчикам
|
# ВАЖНО: Проверяем на мат ДО передачи другим обработчикам
|
||||||
# Это позволяет auto_mute работать независимо от karma_tracker
|
# Это позволяет auto_mute работать независимо от karma_tracker
|
||||||
await self._check_profanity(message)
|
await self._check_profanity(message)
|
||||||
@@ -90,6 +106,70 @@ class UserUpdateMiddleware(BaseMiddleware):
|
|||||||
)
|
)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
# Проверка slow mode на основе кармы (вызывается в middleware)
|
||||||
|
async def _check_slow_mode(self, message):
|
||||||
|
"""
|
||||||
|
Проверяет, может ли пользователь отправить сообщение согласно slow mode.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если сообщение заблокировано, False если разрешено
|
||||||
|
"""
|
||||||
|
from karma_slow_mode import check_slow_mode, format_time
|
||||||
|
|
||||||
|
# Только для групповых чатов
|
||||||
|
if message.chat.type not in ['group', 'supergroup']:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Не проверяем команды
|
||||||
|
if not message.text or message.text.startswith('/'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Проверяем, является ли пользователь администратором
|
||||||
|
try:
|
||||||
|
chat_member = await self.bot.get_chat_member(message.chat.id, message.from_user.id)
|
||||||
|
if chat_member.status in ['administrator', 'creator']:
|
||||||
|
# Администраторы не подчиняются slow mode
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка проверки статуса пользователя для slow mode: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Получаем карму пользователя
|
||||||
|
user_karma = self.db.get_karma(message.from_user.id, message.chat.id)
|
||||||
|
|
||||||
|
# Проверяем slow mode
|
||||||
|
can_send, remaining_time = check_slow_mode(message.from_user.id, message.chat.id, user_karma)
|
||||||
|
|
||||||
|
if not can_send:
|
||||||
|
# Удаляем сообщение
|
||||||
|
try:
|
||||||
|
await self.bot.delete_message(message.chat.id, message.message_id)
|
||||||
|
logger.info(f"[SLOW MODE] Удалено сообщение от {message.from_user.id} (карма: {user_karma}, осталось ждать: {remaining_time}с)")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Не удалось удалить сообщение slow mode: {e}")
|
||||||
|
|
||||||
|
# Отправляем уведомление (и сразу удаляем через 5 секунд)
|
||||||
|
try:
|
||||||
|
notification = await self.bot.send_message(
|
||||||
|
chat_id=message.chat.id,
|
||||||
|
text=f"⏱ <b>{message.from_user.first_name}</b>, подождите ещё <b>{format_time(remaining_time)}</b> перед отправкой следующего сообщения.\n\n"
|
||||||
|
f"💡 Ваша карма: <b>{user_karma}</b>. Повышайте карму, чтобы уменьшить задержку!",
|
||||||
|
message_thread_id=message.message_thread_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Удаляем уведомление через 5 секунд
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
try:
|
||||||
|
await self.bot.delete_message(message.chat.id, notification.message_id)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Ошибка отправки уведомления slow mode: {e}")
|
||||||
|
|
||||||
|
return True # Сообщение заблокировано
|
||||||
|
|
||||||
|
return False # Сообщение разрешено
|
||||||
|
|
||||||
# Проверка на нецензурную лексику (вызывается в middleware)
|
# Проверка на нецензурную лексику (вызывается в middleware)
|
||||||
async def _check_profanity(self, message):
|
async def _check_profanity(self, message):
|
||||||
"""Проверяет сообщение на мат и применяет мут если нужно"""
|
"""Проверяет сообщение на мат и применяет мут если нужно"""
|
||||||
|
@@ -119,18 +119,6 @@ def register_handlers(bot: AsyncTeleBot):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при обработке реакции: {e}", exc_info=True)
|
logger.error(f"Ошибка при обработке реакции: {e}", exc_info=True)
|
||||||
|
|
||||||
# Обработчик всех текстовых сообщений для кэширования
|
|
||||||
@bot.message_handler(content_types=['text'])
|
|
||||||
async def cache_messages(message: Message):
|
|
||||||
"""Кэширует все текстовые сообщения для возможности обработки реакций"""
|
|
||||||
try:
|
|
||||||
# Кэшируем только сообщения в групповых чатах
|
|
||||||
if message.chat.type in ['group', 'supergroup']:
|
|
||||||
_cache_message(message.chat.id, message.message_id, message.from_user.id)
|
|
||||||
logger.debug(f"[CACHE] Сообщение {message.message_id} от {message.from_user.id} добавлено в кэш")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Ошибка кэширования сообщения: {e}")
|
|
||||||
|
|
||||||
@bot.message_handler(func=lambda message: message.reply_to_message is not None and message.text and not message.text.startswith('/'))
|
@bot.message_handler(func=lambda message: message.reply_to_message is not None and message.text and not message.text.startswith('/'))
|
||||||
async def handle_thank_message(message: Message):
|
async def handle_thank_message(message: Message):
|
||||||
"""
|
"""
|
||||||
|
@@ -90,6 +90,10 @@ async def apply_mute(bot: AsyncTeleBot, message: Message, user_id: int, duration
|
|||||||
until_date=until_date
|
until_date=until_date
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Снимаем карму за автомут
|
||||||
|
db.add_karma(user_id, message.chat.id, -10)
|
||||||
|
logger.info(f"Снято 10 кармы пользователю {user_id} за автомут")
|
||||||
|
|
||||||
# Удаляем сообщение с матом
|
# Удаляем сообщение с матом
|
||||||
try:
|
try:
|
||||||
await bot.delete_message(chat_id=message.chat.id, message_id=message.message_id)
|
await bot.delete_message(chat_id=message.chat.id, message_id=message.message_id)
|
||||||
|
@@ -279,6 +279,10 @@ async def mute_command(bot: AsyncTeleBot, message: Message, photo_path: str = No
|
|||||||
until_date=until_date
|
until_date=until_date
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Снимаем карму за мут
|
||||||
|
db.add_karma(target_user.id, message.chat.id, -10)
|
||||||
|
logger.info(f"Снято 10 кармы пользователю {target_user.id} за мут")
|
||||||
|
|
||||||
# Форматируем время в удобный формат
|
# Форматируем время в удобный формат
|
||||||
time_display = format_mute_time(mute_seconds)
|
time_display = format_mute_time(mute_seconds)
|
||||||
|
|
||||||
|
@@ -144,6 +144,10 @@ async def warn_command(bot: AsyncTeleBot, message: Message):
|
|||||||
admin_id=message.from_user.id
|
admin_id=message.from_user.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Снимаем карму за предупреждение
|
||||||
|
db.add_karma(target_user.id, message.chat.id, -5)
|
||||||
|
logger.info(f"Снято 5 кармы пользователю {target_user.id} за предупреждение")
|
||||||
|
|
||||||
# Импортируем константы времени
|
# Импортируем константы времени
|
||||||
from config import ONE_WEEK, TWO_WEEKS
|
from config import ONE_WEEK, TWO_WEEKS
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user