228 lines
8.8 KiB
Python
228 lines
8.8 KiB
Python
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()
|