diff --git a/bots/handlers/room_handler.py b/bots/handlers/room_handler.py new file mode 100644 index 0000000..61568b9 --- /dev/null +++ b/bots/handlers/room_handler.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +""" +Обработчик MUC комнат (Multi-User Chat). +- Автоматический вход по приглашению (даже если бот перезагружен – комната сохраняется в БД). +- Периодическая проверка присутствия в комнатах и переподключение при необходимости. +- Сохранение JID комнат в PostgreSQL для восстановления после рестарта. + +ВНИМАНИЕ: Все JID комнат должны храниться и сравниваться как строки (bare JID), +чтобы избежать ошибок типов при работе с asyncpg и slixmpp. +""" + +import logging +import asyncio + +logger = logging.getLogger(__name__) + + +class RoomHandler: + """Управление комнатами: приглашения, реджоин, сохранение в БД.""" + + def __init__(self, bot): + self.bot = bot + + async def accept_invite(self, inv): + """ + Обработчик события groupchat_invite. + Автоматически входит в комнату, добавляет её в список rooms и сохраняет в БД. + + Аргументы: + inv: объект приглашения (содержит атрибуты 'room' или 'from') + """ + room = inv.get('room', inv.get('from')) + if not room: + logger.warning("Приглашение не содержит идентификатора комнаты") + return + + # Преобразуем в строку bare JID (без ресурса) + if hasattr(room, 'bare'): + room_str = room.bare + else: + room_str = str(room) + + logger.info(f"Приглашение в комнату {room_str}, входим.") + + # Если комната ещё не в списке бота – добавляем + if room_str not in self.bot.rooms: + self.bot.rooms.append(room_str) + + # Входим в MUC-комнату + await self.bot.plugin['xep_0045'].join_muc(room_str, self.bot.boundjid.user) + + async def rejoin_loop(self): + """ + Бесконечный цикл переподключения к комнатам. + Проверяет, в каких комнатах бот состоит, и если его нет – переподключается. + """ + while True: + await asyncio.sleep(60) + # Получаем список комнат, где бот присутствует + joined_rooms = set() + try: + if hasattr(self.bot.plugin['xep_0045'], 'get_joined_rooms'): + joined_rooms = set(self.bot.plugin['xep_0045'].get_joined_rooms()) + else: + joined_rooms = {str(r) for r in self.bot.plugin['xep_0045'].rooms.keys()} + except Exception as e: + logger.debug(f"Ошибка получения списка комнат: {e}") + continue + + for room in self.bot.rooms: + if room not in joined_rooms: + logger.info(f"Попытка повторного входа в комнату {room}...") + try: + await self.bot.plugin['xep_0045'].join_muc(room, self.bot.boundjid.user) + await asyncio.sleep(1) + except Exception as e: + logger.error(f"Ошибка входа в {room}: {e}") + + async def on_kicked(self, presence): + """Обработчик события, когда бота исключают из комнаты.""" + room = presence['from'].bare + logger.info(f"Бот был исключён из комнаты {room}") + if room in self.bot.rooms: + self.bot.rooms.remove(room) + + async def on_banned(self, presence): + """Обработчик события, когда бота банят в комнате.""" + room = presence['from'].bare + logger.info(f"Бот был забанен в комнате {room}") + if room in self.bot.rooms: + self.bot.rooms.remove(room) \ No newline at end of file