init commit

This commit is contained in:
2025-12-18 05:55:32 +09:00
commit a6817e487e
72 changed files with 13847 additions and 0 deletions

View File

@@ -0,0 +1,281 @@
import logging
import os
from typing import List, Optional, Dict
from telethon import TelegramClient, events
from telethon.tl.types import ChatMember, User
from telethon.errors import (
FloodWaitError, UserDeactivatedError, ChatAdminRequiredError,
PeerIdInvalidError, ChannelInvalidError, UserNotParticipantError
)
from app.settings import Config
logger = logging.getLogger(__name__)
class TelethonClientManager:
"""Менеджер для работы с Telethon клиентом"""
def __init__(self):
self.client: Optional[TelegramClient] = None
self.is_initialized = False
async def initialize(self) -> bool:
"""Инициализировать Telethon клиент"""
try:
if not Config.USE_TELETHON:
logger.warning("Telethon отключен в конфигурации")
return False
if not (Config.TELETHON_API_ID and Config.TELETHON_API_HASH):
logger.error("TELETHON_API_ID или TELETHON_API_HASH не установлены")
return False
# Получить путь для сессии
session_dir = os.path.join(os.path.dirname(__file__), '..', 'sessions')
os.makedirs(session_dir, exist_ok=True)
session_path = os.path.join(session_dir, 'telethon_session')
self.client = TelegramClient(
session_path,
api_id=Config.TELETHON_API_ID,
api_hash=Config.TELETHON_API_HASH
)
await self.client.connect()
# Проверить авторизацию
if not await self.client.is_user_authorized():
logger.error("Telethon клиент не авторизован. Требуется повторный вход")
return False
self.is_initialized = True
me = await self.client.get_me()
logger.info(f"✅ Telethon клиент инициализирован: {me.first_name}")
return True
except Exception as e:
logger.error(f"Ошибка при инициализации Telethon: {e}")
return False
async def shutdown(self):
"""Остановить Telethon клиент"""
if self.client and self.is_initialized:
try:
await self.client.disconnect()
self.is_initialized = False
logger.info("✅ Telethon клиент остановлен")
except Exception as e:
logger.error(f"Ошибка при остановке Telethon: {e}")
async def send_message(self, chat_id: int, text: str,
parse_mode: str = "html",
disable_web_page_preview: bool = True) -> Optional[int]:
"""
Отправить сообщение в чат
Returns:
Optional[int]: ID отправленного сообщения или None при ошибке
"""
if not self.is_initialized:
logger.error("Telethon клиент не инициализирован")
return None
try:
message = await self.client.send_message(
chat_id,
text,
parse_mode=parse_mode,
link_preview=not disable_web_page_preview
)
logger.info(f"✅ Сообщение отправлено в чат {chat_id} (Telethon)")
return message.id
except FloodWaitError as e:
logger.warning(f"⏳ FloodWait: нужно ждать {e.seconds} секунд")
raise
except (ChatAdminRequiredError, UserNotParticipantError):
logger.error(f"❌ Клиент не администратор или не участник чата {chat_id}")
return None
except PeerIdInvalidError:
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[Dict]:
"""
Получить список участников чата
Returns:
List[Dict]: Список участников с информацией
"""
if not self.is_initialized:
logger.error("Telethon клиент не инициализирован")
return []
try:
members = []
async for member in self.client.iter_participants(chat_id, limit=limit):
member_info = {
'user_id': str(member.id),
'username': member.username,
'first_name': member.first_name,
'last_name': member.last_name,
'is_bot': member.bot,
'is_admin': member.is_self, # self-check для упрощения
}
members.append(member_info)
logger.info(f"✅ Получено {len(members)} участников из чата {chat_id}")
return members
except (ChatAdminRequiredError, UserNotParticipantError):
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("Telethon клиент не инициализирован")
return None
try:
chat = await self.client.get_entity(chat_id)
members_count = None
if hasattr(chat, 'participants_count'):
members_count = chat.participants_count
return {
'id': chat.id,
'title': chat.title if hasattr(chat, 'title') else str(chat.id),
'description': chat.about if hasattr(chat, 'about') else None,
'members_count': members_count,
'is_supergroup': hasattr(chat, 'megagroup') and chat.megagroup,
'is_channel': hasattr(chat, 'broadcast'),
'is_group': hasattr(chat, 'gigagroup')
}
except Exception as e:
logger.error(f"❌ Ошибка при получении информации о чате {chat_id}: {e}")
return None
async def join_chat(self, chat_link: str) -> Optional[int]:
"""
Присоединиться к чату по ссылке
Returns:
Optional[int]: ID чата или None при ошибке
"""
if not self.is_initialized:
logger.error("Telethon клиент не инициализирован")
return None
try:
# Попытаться присоединиться
result = await self.client(ImportChatInviteRequest(hash))
chat_id = result.chats[0].id
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("Telethon клиент не инициализирован")
return False
try:
await self.client.delete_dialog(chat_id, revoke=True)
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[int]:
"""
Отредактировать сообщение
Returns:
Optional[int]: ID отредактированного сообщения или None при ошибке
"""
if not self.is_initialized:
logger.error("Telethon клиент не инициализирован")
return None
try:
message = await self.client.edit_message(
chat_id,
message_id,
text,
parse_mode="html"
)
logger.info(f"✅ Сообщение отредактировано: {chat_id}/{message_id}")
return message.id
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("Telethon клиент не инициализирован")
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[Dict]:
"""
Искать сообщения в чате
Returns:
List[Dict]: Список найденных сообщений
"""
if not self.is_initialized:
logger.error("Telethon клиент не инициализирован")
return []
try:
messages = []
async for message in self.client.iter_messages(chat_id, search=query, limit=limit):
messages.append({
'id': message.id,
'text': message.text,
'date': message.date
})
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
# Глобальный экземпляр менеджера
telethon_manager = TelethonClientManager()