import logging from typing import List, Optional, Dict from pyrogram import Client from pyrogram.types import Message, ChatMember from pyrogram.errors import ( FloodWait, UserDeactivated, ChatAdminRequired, PeerIdInvalid, ChannelInvalid, UserNotParticipant ) from app.settings import Config logger = logging.getLogger(__name__) class PyrogramClientManager: """Менеджер для работы с Pyrogram клиентом""" def __init__(self): self.client: Optional[Client] = None self.is_initialized = False async def initialize(self) -> bool: """Инициализировать Pyrogram клиент""" try: if not Config.USE_PYROGRAM: logger.warning("Pyrogram отключен в конфигурации") return False if not (Config.PYROGRAM_API_ID and Config.PYROGRAM_API_HASH): logger.error("PYROGRAM_API_ID или PYROGRAM_API_HASH не установлены") return False self.client = Client( name="tg_autoposter", api_id=Config.PYROGRAM_API_ID, api_hash=Config.PYROGRAM_API_HASH, phone_number=Config.PYROGRAM_PHONE ) await self.client.start() self.is_initialized = True me = await self.client.get_me() logger.info(f"Pyrogram клиент инициализирован: {me.first_name}") return True except Exception as e: logger.error(f"Ошибка при инициализации Pyrogram: {e}") return False async def shutdown(self): """Остановить Pyrogram клиент""" if self.client and self.is_initialized: try: await self.client.stop() self.is_initialized = False logger.info("Pyrogram клиент остановлен") except Exception as e: logger.error(f"Ошибка при остановке Pyrogram: {e}") async def send_message(self, chat_id: int, text: str, parse_mode: str = "html", disable_web_page_preview: bool = True) -> Optional[Message]: """Отправить сообщение в чат""" if not self.is_initialized: logger.error("Pyrogram клиент не инициализирован") return None try: message = await self.client.send_message( chat_id=chat_id, text=text, parse_mode=parse_mode, disable_web_page_preview=disable_web_page_preview ) logger.info(f"Сообщение отправлено в чат {chat_id} (клиент)") return message except FloodWait as e: logger.warning(f"FloodWait: нужно ждать {e.value} секунд") raise except (ChatAdminRequired, UserNotParticipant): logger.error(f"Клиент не администратор или не участник чата {chat_id}") return None except PeerIdInvalid: logger.error(f"Неверный ID чата: {chat_id}") return None except Exception as e: logger.error(f"Ошибка при отправке сообщения: {e}") return None async def get_chat_members(self, chat_id: int, limit: int = None) -> List[ChatMember]: """Получить список участников чата""" if not self.is_initialized: logger.error("Pyrogram клиент не инициализирован") return [] try: members = [] async for member in self.client.get_chat_members(chat_id): members.append(member) if limit and len(members) >= limit: break logger.info(f"Получено {len(members)} участников из чата {chat_id}") return members except (ChatAdminRequired, UserNotParticipant): logger.error(f"Нет прав получить участников чата {chat_id}") return [] except Exception as e: logger.error(f"Ошибка при получении участников: {e}") return [] async def get_chat_info(self, chat_id: int) -> Optional[Dict]: """Получить информацию о чате""" if not self.is_initialized: logger.error("Pyrogram клиент не инициализирован") return None try: chat = await self.client.get_chat(chat_id) return { 'id': chat.id, 'title': chat.title, 'description': getattr(chat, 'description', None), 'members_count': getattr(chat, 'members_count', None), 'is_supergroup': chat.is_supergroup, 'linked_chat': getattr(chat, 'linked_chat_id', None) } except Exception as e: logger.error(f"Ошибка при получении информации о чате {chat_id}: {e}") return None async def join_chat(self, chat_link: str) -> Optional[int]: """Присоединиться к чату по ссылке""" if not self.is_initialized: logger.error("Pyrogram клиент не инициализирован") return None try: chat = await self.client.join_chat(chat_link) logger.info(f"Присоединился к чату: {chat.id}") return chat.id except Exception as e: logger.error(f"Ошибка при присоединении к чату: {e}") return None async def leave_chat(self, chat_id: int) -> bool: """Покинуть чат""" if not self.is_initialized: logger.error("Pyrogram клиент не инициализирован") return False try: await self.client.leave_chat(chat_id) logger.info(f"Покинул чат: {chat_id}") return True except Exception as e: logger.error(f"Ошибка при выходе из чата: {e}") return False async def edit_message(self, chat_id: int, message_id: int, text: str) -> Optional[Message]: """Отредактировать сообщение""" if not self.is_initialized: logger.error("Pyrogram клиент не инициализирован") return None try: message = await self.client.edit_message_text( chat_id=chat_id, message_id=message_id, text=text, parse_mode="html" ) logger.info(f"Сообщение отредактировано: {chat_id}/{message_id}") return message except Exception as e: logger.error(f"Ошибка при редактировании сообщения: {e}") return None async def delete_message(self, chat_id: int, message_id: int) -> bool: """Удалить сообщение""" if not self.is_initialized: logger.error("Pyrogram клиент не инициализирован") return False try: await self.client.delete_messages(chat_id, message_id) logger.info(f"Сообщение удалено: {chat_id}/{message_id}") return True except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") return False async def search_messages(self, chat_id: int, query: str, limit: int = 100) -> List[Message]: """Искать сообщения в чате""" if not self.is_initialized: logger.error("Pyrogram клиент не инициализирован") return [] try: messages = [] async for message in self.client.search_messages(chat_id, query=query, limit=limit): messages.append(message) logger.info(f"Найдено {len(messages)} сообщений по запросу '{query}'") return messages except Exception as e: logger.error(f"Ошибка при поиске сообщений: {e}") return [] def is_connected(self) -> bool: """Проверить, подключен ли клиент""" return self.is_initialized and self.client is not None # Глобальный экземпляр менеджера pyrogram_manager = PyrogramClientManager()