diff --git a/.gigaide/gigaide.properties b/.gigaide/gigaide.properties
index ca124ef..3adf633 100644
--- a/.gigaide/gigaide.properties
+++ b/.gigaide/gigaide.properties
@@ -1,5 +1,5 @@
-## changed at Sun Oct 19 13:23:17 MSK 2025
-#Sun Oct 19 13:23:17 MSK 2025
+## changed at Sun Oct 19 17:30:33 MSK 2025
+#Sun Oct 19 17:30:33 MSK 2025
com.gigaide.elements.ext.marker.solution.BeanMarkedPsi.shouldMark=true
com.gigaide.elements.ext.marker.solution.ConfigMarkedPsi.shouldMark=true
com.gigaide.elements.ext.marker.solution.DataMarkedPsi.shouldMark=true
diff --git a/src/main.py b/src/main.py
index 8fa75ed..a14917a 100644
--- a/src/main.py
+++ b/src/main.py
@@ -16,6 +16,8 @@ from action_reporter import init_action_reporter
from config import MODULES_DIR
+from message_queue import init_message_queue, add_to_queue
+
# Загружаем токен бота из .env
load_dotenv()
@@ -143,7 +145,24 @@ class UserUpdateMiddleware(BaseMiddleware):
can_send, remaining_time = check_slow_mode(message.from_user.id, message.chat.id, user_karma)
if not can_send:
- # Удаляем сообщение
+ # Сохраняем текст сообщения перед удалением
+ message_text = message.text
+
+ # Добавляем сообщение в очередь для отправки после задержки
+ try:
+ add_to_queue(
+ chat_id=message.chat.id,
+ user_id=message.from_user.id,
+ user_name=message.from_user.first_name,
+ text=message_text,
+ delay_seconds=remaining_time,
+ thread_id=message.message_thread_id
+ )
+ logger.info(f"[SLOW MODE] Сообщение от {message.from_user.id} добавлено в очередь на {remaining_time}с")
+ except Exception as e:
+ logger.error(f"Ошибка добавления сообщения в очередь: {e}")
+
+ # Удаляем оригинальное сообщение
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}с)")
@@ -154,7 +173,7 @@ class UserUpdateMiddleware(BaseMiddleware):
try:
notification = await self.bot.send_message(
chat_id=message.chat.id,
- text=f"⏱ {message.from_user.first_name}, подождите ещё {format_time(remaining_time)} перед отправкой следующего сообщения.\n\n"
+ text=f"⏱ {message.from_user.first_name}, ваше сообщение будет отправлено через {format_time(remaining_time)}.\n\n"
f"💡 Ваша карма: {user_karma}. Повышайте карму, чтобы уменьшить задержку!",
message_thread_id=message.message_thread_id,
)
@@ -283,6 +302,10 @@ async def main():
# Устанавливаем команды бота
await setup_bot_commands()
+ # Инициализируем систему очереди сообщений
+ init_message_queue(bot)
+ logger.info("Система очереди сообщений инициализирована")
+
# Запускаем бота с обработкой реакций
logger.info("Запуск бота с allowed_updates: message, message_reaction, chat_member")
await bot.infinity_polling(allowed_updates=['message', 'message_reaction', 'chat_member'])
diff --git a/src/message_queue.py b/src/message_queue.py
new file mode 100644
index 0000000..7ebdd6c
--- /dev/null
+++ b/src/message_queue.py
@@ -0,0 +1,150 @@
+# Очередь отложенных сообщений для медленного режима
+# Сохраняет сообщения и отправляет их после истечения задержки
+
+import asyncio
+import time
+import logging
+from typing import Dict, List, Optional
+from dataclasses import dataclass
+from collections import deque
+
+logger = logging.getLogger(__name__)
+
+@dataclass
+class QueuedMessage:
+ """Сообщение в очереди"""
+ chat_id: int
+ user_id: int
+ user_name: str
+ text: str
+ send_time: float # Unix timestamp когда нужно отправить
+ thread_id: Optional[int] = None
+
+# Очередь сообщений для каждого чата
+_message_queues: Dict[int, deque[QueuedMessage]] = {}
+_processing_task: Optional[asyncio.Task] = None
+_bot_instance = None
+
+def init_message_queue(bot):
+ """
+ Инициализирует систему очереди сообщений.
+
+ Args:
+ bot: Экземпляр бота для отправки сообщений
+ """
+ global _bot_instance, _processing_task
+ _bot_instance = bot
+
+ # Запускаем фоновую задачу обработки очереди, если она ещё не запущена
+ if _processing_task is None or _processing_task.done():
+ _processing_task = asyncio.create_task(_process_queue())
+ logger.info("Система очереди сообщений инициализирована")
+
+def add_to_queue(chat_id: int, user_id: int, user_name: str, text: str, delay_seconds: int, thread_id: Optional[int] = None):
+ """
+ Добавляет сообщение в очередь для отправки после задержки.
+
+ Args:
+ chat_id: ID чата
+ user_id: ID пользователя
+ user_name: Имя пользователя
+ text: Текст сообщения
+ delay_seconds: Задержка в секундах перед отправкой
+ thread_id: ID топика (для супергрупп с топиками)
+ """
+ send_time = time.time() + delay_seconds
+
+ message = QueuedMessage(
+ chat_id=chat_id,
+ user_id=user_id,
+ user_name=user_name,
+ text=text,
+ send_time=send_time,
+ thread_id=thread_id
+ )
+
+ # Добавляем очередь для чата, если её ещё нет
+ if chat_id not in _message_queues:
+ _message_queues[chat_id] = deque()
+
+ _message_queues[chat_id].append(message)
+ logger.info(f"[QUEUE] Сообщение от {user_name} ({user_id}) добавлено в очередь чата {chat_id}, отправка через {delay_seconds}с")
+
+async def _process_queue():
+ """
+ Фоновая задача для обработки очереди сообщений.
+ Проверяет каждую секунду, есть ли сообщения готовые к отправке.
+ """
+ global _bot_instance
+ logger.info("[QUEUE] Запущена фоновая задача обработки очереди")
+
+ while True:
+ try:
+ current_time = time.time()
+
+ # Проходим по всем чатам
+ for chat_id in list(_message_queues.keys()):
+ queue = _message_queues[chat_id]
+
+ # Обрабатываем сообщения, которые готовы к отправке
+ while queue and queue[0].send_time <= current_time:
+ message = queue.popleft()
+
+ try:
+ # Отправляем сообщение
+ formatted_text = f"💬 {message.user_name}:\n{message.text}"
+
+ await _bot_instance.send_message(
+ chat_id=message.chat_id,
+ text=formatted_text,
+ message_thread_id=message.thread_id
+ )
+
+ logger.info(f"[QUEUE] Сообщение от {message.user_name} ({message.user_id}) отправлено в чат {message.chat_id}")
+
+ except Exception as e:
+ logger.error(f"[QUEUE] Ошибка отправки сообщения из очереди: {e}", exc_info=True)
+
+ # Удаляем пустые очереди
+ if not queue:
+ del _message_queues[chat_id]
+
+ # Ждём 1 секунду перед следующей проверкой
+ await asyncio.sleep(1)
+
+ except Exception as e:
+ logger.error(f"[QUEUE] Ошибка в обработке очереди: {e}", exc_info=True)
+ await asyncio.sleep(1)
+
+def get_queue_size(chat_id: int) -> int:
+ """
+ Возвращает количество сообщений в очереди для чата.
+
+ Args:
+ chat_id: ID чата
+
+ Returns:
+ Количество сообщений в очереди
+ """
+ return len(_message_queues.get(chat_id, []))
+
+def get_user_queue_position(chat_id: int, user_id: int) -> Optional[int]:
+ """
+ Возвращает позицию пользователя в очереди (1-based).
+
+ Args:
+ chat_id: ID чата
+ user_id: ID пользователя
+
+ Returns:
+ Позиция в очереди или None если нет сообщений
+ """
+ if chat_id not in _message_queues:
+ return None
+
+ queue = _message_queues[chat_id]
+ for i, msg in enumerate(queue):
+ if msg.user_id == user_id:
+ return i + 1
+
+ return None
\ No newline at end of file