"""
Обработчик управления UserBot для сбора групп и участников
"""
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import ContextTypes, ConversationHandler
from app.userbot.parser import userbot_parser
from app.database import AsyncSessionLocal
from app.database.repository import GroupRepository
from app.database.member_repository import GroupMemberRepository
from app.utils.keyboards import CallbackType
import logging
logger = logging.getLogger(__name__)
# Состояния для ConversationHandler
USERBOT_MENU = 1
USERBOT_SETTINGS = 2
USERBOT_COLLECTING_GROUPS = 3
USERBOT_SELECT_GROUP = 4
USERBOT_COLLECTING_MEMBERS = 5
async def userbot_menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Меню управления UserBot"""
try:
query = update.callback_query
if query:
await query.answer()
text = """🤖 UserBot - Управление парсингом
Что вы хотите сделать?
UserBot собирает информацию о группах и их участниках от имени пользователя"""
keyboard = [
[InlineKeyboardButton("⚙️ Настройки", callback_data="userbot_settings")],
[InlineKeyboardButton("📥 Собрать группы", callback_data="userbot_collect_groups")],
[InlineKeyboardButton("👥 Собрать участников", callback_data="userbot_collect_members")],
[InlineKeyboardButton("⬅️ Назад в меню", callback_data=CallbackType.MAIN_MENU.value)],
]
if query:
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
else:
await update.message.reply_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_MENU
except Exception as e:
logger.error(f"❌ Ошибка в userbot_menu: {e}", exc_info=True)
return ConversationHandler.END
async def userbot_settings(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Настройки UserBot"""
try:
query = update.callback_query
await query.answer()
# Проверяем статус UserBot
is_initialized = userbot_parser.is_initialized
status = "✅ Инициализирован" if is_initialized else "❌ Не инициализирован"
text = f"""⚙️ Настройки UserBot
Статус: {status}
Возможности:
• Собирать информацию о группах
• Собирать списки участников
• Сохранять данные в БД
• Работать в фоне через Celery
Нажмите кнопку для продолжения"""
keyboard = [
[InlineKeyboardButton("🔄 Инициализировать", callback_data="userbot_init")],
[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_menu")],
]
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_SETTINGS
except Exception as e:
logger.error(f"❌ Ошибка в userbot_settings: {e}", exc_info=True)
return ConversationHandler.END
async def userbot_init(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Инициализация UserBot"""
try:
query = update.callback_query
await query.answer()
await query.edit_message_text(
"⏳ Инициализирую UserBot...",
parse_mode='HTML'
)
# Инициализируем UserBot
success = await userbot_parser.initialize()
if success:
text = """✅ UserBot успешно инициализирован!
Теперь вы можете:
• Собирать информацию о группах
• Собирать списки участников
• Синхронизировать данные в БД
Перейдите в меню для продолжения."""
keyboard = [[InlineKeyboardButton("🔄 Меню", callback_data="userbot_menu")]]
else:
text = """❌ Ошибка инициализации UserBot
Убедитесь, что:
• Переменные окружения установлены
• TELETHON_API_ID и TELETHON_API_HASH верные
• Сессия сохранена в sessions/userbot_session.session
Попробуйте позже."""
keyboard = [[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_settings")]]
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_SETTINGS
except Exception as e:
logger.error(f"❌ Ошибка инициализации UserBot: {e}", exc_info=True)
text = f"""❌ Ошибка при инициализации:
{str(e)[:200]}
Проверьте логи бота."""
keyboard = [[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_settings")]]
query = update.callback_query
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_SETTINGS
async def userbot_collect_groups(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Сбор групп от пользователя"""
try:
query = update.callback_query
await query.answer()
if not userbot_parser.is_initialized:
text = "❌ UserBot не инициализирован!\n\nПерейдите в настройки для инициализации."
keyboard = [[InlineKeyboardButton("⚙️ Настройки", callback_data="userbot_settings")]]
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_MENU
await query.edit_message_text(
"⏳ Собираю информацию о группах...\n\nЭто может занять некоторое время...",
parse_mode='HTML'
)
# Собираем группы
logger.info(f"📥 Начало сбора групп для пользователя {update.effective_user.id}")
groups = await userbot_parser.parse_groups_user_in()
if not groups:
text = "❌ Не найдено групп\n\nУбедитесь, что вы состоите в группах."
keyboard = [[InlineKeyboardButton("🔄 Попробовать снова", callback_data="userbot_collect_groups")],
[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_menu")]]
else:
# Сохраняем группы в контексте
context.user_data['available_groups'] = groups
text = f"""✅ Найдено групп: {len(groups)}
Список групп:"""
for i, group in enumerate(groups, 1):
title = group.get('title', 'Неизвестная группа')
chat_id = group.get('chat_id', 'Unknown')
members = group.get('members_count', 0)
text += f"\n{i}. {title}\n 👥 Участников: {members}"
text += "\n\n💾 Группы сохранены в базе данных!"
# Сохраняем группы в БД
async with AsyncSessionLocal() as session:
repo = GroupRepository(session)
for group in groups:
await repo.add_or_update_group({
'chat_id': group.get('chat_id'),
'title': group.get('title'),
'description': group.get('description', ''),
'members_count': group.get('members_count', 0),
'is_active': True
})
logger.info(f"✅ Сохранено {len(groups)} групп")
keyboard = [[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_menu")]]
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_COLLECTING_GROUPS
except Exception as e:
logger.error(f"❌ Ошибка при сборе групп: {e}", exc_info=True)
text = f"""❌ Ошибка при сборе групп:
{str(e)[:200]}"""
keyboard = [[InlineKeyboardButton("🔄 Попробовать снова", callback_data="userbot_collect_groups")],
[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_menu")]]
query = update.callback_query
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_COLLECTING_GROUPS
async def userbot_collect_members(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Выбор группы для сбора участников"""
try:
query = update.callback_query
await query.answer()
if not userbot_parser.is_initialized:
text = "❌ UserBot не инициализирован!\n\nПерейдите в настройки для инициализации."
keyboard = [[InlineKeyboardButton("⚙️ Настройки", callback_data="userbot_settings")]]
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_MENU
# Получаем группы из БД
async with AsyncSessionLocal() as session:
repo = GroupRepository(session)
groups = await repo.get_active_groups()
if not groups:
text = "❌ Не найдено активных групп\n\nСначала соберите информацию о группах."
keyboard = [[InlineKeyboardButton("📥 Собрать группы", callback_data="userbot_collect_groups")],
[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_menu")]]
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_SELECT_GROUP
text = """👥 Выберите группу для сбора участников
Нажмите на группу для сбора списка участников:"""
keyboard = []
for group in groups:
title = group.title if hasattr(group, 'title') else group.get('title', 'Unknown')
group_id = group.id if hasattr(group, 'id') else group.get('id', 0)
callback_data = f"userbot_members_{group_id}"
keyboard.append([InlineKeyboardButton(f"👥 {title}", callback_data=callback_data)])
keyboard.append([InlineKeyboardButton("⬅️ Назад", callback_data="userbot_menu")])
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_SELECT_GROUP
except Exception as e:
logger.error(f"❌ Ошибка при выборе группы: {e}", exc_info=True)
text = f"""❌ Ошибка:
{str(e)[:200]}"""
keyboard = [[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_menu")]]
query = update.callback_query
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_SELECT_GROUP
async def userbot_parse_members(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Сбор участников для выбранной группы"""
try:
query = update.callback_query
await query.answer()
# Извлекаем group_id из callback_data
group_id = int(query.data.replace("userbot_members_", ""))
await query.edit_message_text(
f"⏳ Собираю участников группы...\n\ngroup_id: {group_id}\n\nЭто может занять некоторое время...",
parse_mode='HTML'
)
# Получаем информацию о группе из БД
async with AsyncSessionLocal() as session:
repo = GroupRepository(session)
group = await repo.get_group_by_id(group_id)
if not group:
text = "❌ Группа не найдена в базе данных."
keyboard = [[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_collect_members")]]
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_COLLECTING_MEMBERS
chat_id = group.chat_id if hasattr(group, 'chat_id') else group.get('chat_id')
title = group.title if hasattr(group, 'title') else group.get('title', 'Unknown')
logger.info(f"👥 Начало сбора участников для группы {title} (chat_id: {chat_id})")
# Собираем участников
members = await userbot_parser.parse_group_members(chat_id=chat_id, limit=10000)
if not members:
text = f"❌ Не удалось собрать участников группы:\n{title}"
keyboard = [[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_collect_members")]]
else:
# Сохраняем участников в БД
async with AsyncSessionLocal() as session:
member_repo = GroupMemberRepository(session)
for member in members:
await member_repo.add_or_update_member({
'group_id': group_id,
'user_id': member.get('user_id'),
'username': member.get('username'),
'first_name': member.get('first_name'),
'last_name': member.get('last_name'),
'is_bot': member.get('is_bot', False),
'is_admin': member.get('is_admin', False),
'is_owner': member.get('is_owner', False),
})
# Статистика
total = len(members)
bots = len([m for m in members if m.get('is_bot')])
admins = len([m for m in members if m.get('is_admin')])
owners = len([m for m in members if m.get('is_owner')])
text = f"""✅ Участники собраны!
Группа: {title}
Статистика:
• 👥 Всего участников: {total}
• 🤖 Ботов: {bots}
• 👑 Администраторов: {admins}
• 🔑 Владельцев: {owners}
💾 Данные сохранены в базе данных!"""
logger.info(f"✅ Сохранено {total} участников группы {title}")
keyboard = [[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_collect_members")]]
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_COLLECTING_MEMBERS
except Exception as e:
logger.error(f"❌ Ошибка при сборе участников: {e}", exc_info=True)
text = f"""❌ Ошибка при сборе участников:
{str(e)[:200]}
Это может быть вызвано:
• FloodWait от Telegram
• Недостатком прав доступа
• Проблемой с сессией UserBot"""
keyboard = [[InlineKeyboardButton("⬅️ Назад", callback_data="userbot_collect_members")],
[InlineKeyboardButton("🏠 Меню", callback_data="userbot_menu")]]
query = update.callback_query
await query.edit_message_text(
text,
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return USERBOT_COLLECTING_MEMBERS
async def cancel_userbot(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
"""Отмена диалога"""
query = update.callback_query
if query:
await query.answer()
keyboard = [[InlineKeyboardButton("🏠 Главное меню", callback_data=CallbackType.MAIN_MENU.value)]]
await query.edit_message_text(
"❌ Диалог отменен",
parse_mode='HTML',
reply_markup=InlineKeyboardMarkup(keyboard)
)
return ConversationHandler.END