From 459ed66e9a0969bf44f09175fb21e1255ce06608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=A5=D1=80?= =?UTF-8?q?=D0=B0=D0=BC=D0=BE=D0=B2?= Date: Sun, 19 Oct 2025 19:22:07 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BA=D1=8D=D1=88=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database.py | 65 ++++++++++++++++++++++++++++++++++ src/modules/0_karma_tracker.py | 39 ++++++++++++-------- 2 files changed, 89 insertions(+), 15 deletions(-) diff --git a/src/database.py b/src/database.py index 3e6f287..4585c22 100644 --- a/src/database.py +++ b/src/database.py @@ -92,6 +92,24 @@ class Database: # Инициализация класса ON users(tag COLLATE NOCASE) ''') + # Таблица для кэша сообщений (для обработки реакций) + cursor.execute(''' + CREATE TABLE IF NOT EXISTS message_cache ( + chat_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + thread_id INTEGER, + timestamp INTEGER NOT NULL, + PRIMARY KEY (chat_id, message_id) + ) + ''') + + # Индекс для быстрой очистки старых записей + cursor.execute(''' + CREATE INDEX IF NOT EXISTS idx_message_cache_timestamp + ON message_cache(timestamp) + ''') + connect.commit() logger.info("База данных и индексы успешно инициализированы") @@ -395,5 +413,52 @@ class Database: # Инициализация класса connect.commit() logger.info(f"Пользователь {from_user_id} поблагодарил {to_user_id} в чате {chat_id}") + # Добавляет сообщение в кэш + def cache_message(self, chat_id: int, message_id: int, user_id: int, thread_id: Optional[int] = None): + with self._get_connection() as connect: + cursor = connect.cursor() + current_time = int(time.time()) + cursor.execute(''' + INSERT OR REPLACE INTO message_cache (chat_id, message_id, user_id, thread_id, timestamp) + VALUES (?, ?, ?, ?, ?) + ''', (chat_id, message_id, user_id, thread_id, current_time)) + connect.commit() + + # Получает информацию о сообщении из кэша + # Возвращает (user_id, thread_id) или None если не найдено + def get_cached_message(self, chat_id: int, message_id: int) -> Optional[Tuple[int, Optional[int]]]: + with self._get_connection() as connect: + cursor = connect.cursor() + cursor.execute(''' + SELECT user_id, thread_id + FROM message_cache + WHERE chat_id = ? AND message_id = ? + ''', (chat_id, message_id)) + result = cursor.fetchone() + return result if result else None + + # Очищает сообщения старше указанного времени (по умолчанию 24 часа) + def cleanup_old_messages(self, max_age_seconds: int = 86400): + with self._get_connection() as connect: + cursor = connect.cursor() + cutoff_time = int(time.time()) - max_age_seconds + cursor.execute(''' + DELETE FROM message_cache + WHERE timestamp < ? + ''', (cutoff_time,)) + deleted_count = cursor.rowcount + connect.commit() + if deleted_count > 0: + logger.info(f"Удалено {deleted_count} старых сообщений из кэша") + return deleted_count + + # Получает количество сообщений в кэше + def get_cache_size(self) -> int: + with self._get_connection() as connect: + cursor = connect.cursor() + cursor.execute('SELECT COUNT(*) FROM message_cache') + result = cursor.fetchone() + return result[0] if result else 0 + # Создаем экземпляр базы данных для импорта в других модулях db = Database() \ No newline at end of file diff --git a/src/modules/0_karma_tracker.py b/src/modules/0_karma_tracker.py index 268a0b2..50034bc 100644 --- a/src/modules/0_karma_tracker.py +++ b/src/modules/0_karma_tracker.py @@ -2,7 +2,6 @@ from telebot.async_telebot import AsyncTeleBot from telebot.types import Message, MessageReactionUpdated, ReactionTypeEmoji import asyncio import logging -from collections import OrderedDict from database import db from thank_words import contains_thank_word @@ -11,29 +10,39 @@ from config import THANK_COOLDOWN logger = logging.getLogger(__name__) -# Кэш для хранения message_id -> (user_id, message_thread_id) (последние 1000 сообщений) -# Используем OrderedDict для автоматического удаления старых записей -_message_cache = OrderedDict() -_MAX_CACHE_SIZE = 1000 +# Фоновая задача для автоочистки старых сообщений +_cleanup_task = None def _cache_message(chat_id: int, message_id: int, user_id: int, message_thread_id: int = None): - """Добавляет сообщение в кэш""" - key = f"{chat_id}:{message_id}" - _message_cache[key] = (user_id, message_thread_id) - - # Удаляем старые записи, если кэш переполнен - if len(_message_cache) > _MAX_CACHE_SIZE: - _message_cache.popitem(last=False) + """Добавляет сообщение в кэш БД""" + db.cache_message(chat_id, message_id, user_id, message_thread_id) def _get_cached_message(chat_id: int, message_id: int): - """Получает (user_id, message_thread_id) из кэша""" - key = f"{chat_id}:{message_id}" - return _message_cache.get(key) + """Получает (user_id, message_thread_id) из кэша БД""" + return db.get_cached_message(chat_id, message_id) + +async def _cleanup_old_cache(): + """Фоновая задача для очистки старых сообщений из кэша каждый час""" + while True: + try: + await asyncio.sleep(3600) # Ждём 1 час + deleted = db.cleanup_old_messages(max_age_seconds=86400) # Удаляем старше 24 часов + cache_size = db.get_cache_size() + logger.info(f"[CACHE CLEANUP] Размер кэша: {cache_size} сообщений") + except Exception as e: + logger.error(f"[CACHE CLEANUP] Ошибка очистки кэша: {e}", exc_info=True) def register_handlers(bot: AsyncTeleBot): """Регистрирует обработчики для отслеживания благодарностей""" logger.info("Регистрация обработчика благодарностей (karma_tracker)") + # Запускаем фоновую задачу очистки старых сообщений из кэша + global _cleanup_task + if _cleanup_task is None or _cleanup_task.done(): + _cleanup_task = asyncio.create_task(_cleanup_old_cache()) + cache_size = db.get_cache_size() + logger.info(f"[CACHE] Запущена автоочистка кэша. Текущий размер: {cache_size} сообщений") + @bot.message_reaction_handler(func=lambda m: True) async def handle_reaction(reaction: MessageReactionUpdated): """