✨ Улучшения: ✅ Расширенная обработка ошибок при вводе пароля 2FA ✅ Различие между неверным паролем и другими ошибками ✅ Подробные подсказки для пользователя при ошибках ✅ Поддержка восстановительных кодов 2FA ✅ Улучшенное сообщение при запросе пароля 2FA 📖 Документация: ✅ Создан 2FA_GUIDE.md (подробное руководство) ✅ Обновлена информация о 2FA в боте (auth_info) ✅ Добавлены примеры и советы по использованию 🔐 Обработка ошибок: • Неверный пароль - ясное сообщение + подсказки • Пароль истек - предложение повторить • SMS-код истек - инструкция по получению нового • Много попыток - информация о ограничениях 📱 Процесс с 2FA: 1. Номер телефона 2. SMS-код (5 цифр) 3. Пароль 2FA (если включена) 4. ✅ Авторизация успешна 💡 Основные преимущества: • Ясные объяснения на каждом этапе • Подсказки при забывании пароля • Безопасное обращение с паролями (не сохраняются) • Поддержка восстановительных кодов
191 lines
8.6 KiB
Python
191 lines
8.6 KiB
Python
import os
|
||
import logging
|
||
from dotenv import load_dotenv
|
||
from telegram.ext import (
|
||
Application,
|
||
CommandHandler,
|
||
CallbackQueryHandler,
|
||
ChatMemberHandler,
|
||
MessageHandler,
|
||
ConversationHandler,
|
||
filters,
|
||
)
|
||
from app.database import init_db
|
||
from app.handlers import (
|
||
start,
|
||
help_command,
|
||
sync_groups_command,
|
||
start_callback,
|
||
manage_messages,
|
||
manage_groups,
|
||
list_messages,
|
||
list_groups,
|
||
send_message,
|
||
my_chat_member,
|
||
userbot_menu,
|
||
userbot_settings,
|
||
userbot_init,
|
||
userbot_collect_groups,
|
||
userbot_collect_members,
|
||
userbot_parse_members,
|
||
cancel_userbot,
|
||
auth_menu,
|
||
auth_info,
|
||
start_phone_input,
|
||
handle_phone,
|
||
handle_code,
|
||
handle_password,
|
||
cancel_auth,
|
||
)
|
||
from app.handlers.message_manager import (
|
||
create_message_start,
|
||
create_message_title,
|
||
create_message_text,
|
||
select_groups,
|
||
handle_message_input,
|
||
)
|
||
from app.handlers.telethon_client import telethon_manager
|
||
from app.utils.keyboards import CallbackType
|
||
from app.settings import Config
|
||
|
||
|
||
# Загружаем переменные окружения
|
||
load_dotenv()
|
||
|
||
# Настройка логирования
|
||
logging.basicConfig(
|
||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||
level=logging.DEBUG
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
async def debug_update_handler(update, context):
|
||
"""Обработчик для отладки всех обновлений"""
|
||
logger.info(f"🎯 Получено обновление: {update}")
|
||
if update.message:
|
||
logger.info(f"📨 Сообщение от {update.effective_user.id}: {update.message.text}")
|
||
elif update.callback_query:
|
||
logger.info(f"🔘 Callback от {update.effective_user.id}: {update.callback_query.data}")
|
||
else:
|
||
logger.info(f"❓ Неизвестное обновление: {update.to_dict() if hasattr(update, 'to_dict') else str(update)}")
|
||
return None
|
||
|
||
# Получаем конфигурацию
|
||
# Для UserBot контейнера: если есть TELETHON переменные и нет BOT_TOKEN - это ОК
|
||
is_userbot_only = (
|
||
os.getenv('TELETHON_API_ID')
|
||
and os.getenv('TELETHON_API_HASH')
|
||
and os.getenv('TELETHON_PHONE')
|
||
and not os.getenv('TELEGRAM_BOT_TOKEN')
|
||
)
|
||
|
||
if not is_userbot_only:
|
||
if not Config.validate():
|
||
raise ValueError("❌ Конфигурация некорректна. Проверьте .env файл")
|
||
|
||
|
||
async def main() -> None:
|
||
"""Запуск бота с поддержкой гибридного режима"""
|
||
|
||
# Инициализируем БД
|
||
logger.info("Инициализация базы данных...")
|
||
await init_db()
|
||
logger.info("✅ База данных инициализирована")
|
||
|
||
# Инициализируем Telethon если включен
|
||
if Config.USE_TELETHON:
|
||
logger.info("Инициализация Telethon клиента...")
|
||
success = await telethon_manager.initialize()
|
||
if success:
|
||
logger.info("✅ Telethon клиент инициализирован")
|
||
else:
|
||
logger.warning("⚠️ Ошибка инициализации Telethon, продолжим с режимом бота")
|
||
|
||
# Выводим информацию о режиме
|
||
mode = Config.get_mode()
|
||
logger.info(f"📡 Режим работы: {mode}")
|
||
if mode == 'hybrid':
|
||
logger.info("🔀 Бот будет использовать Telethon как fallback для закрытых групп")
|
||
|
||
# Создаем приложение
|
||
application = Application.builder().token(Config.TELEGRAM_BOT_TOKEN).build()
|
||
|
||
# Добавляем отладчик для всех текстовых сообщений (самый низкий приоритет)
|
||
application.add_handler(MessageHandler(filters.ALL, debug_update_handler), group=100)
|
||
|
||
# Добавляем обработчики команд (высший приоритет, группа 0)
|
||
application.add_handler(CommandHandler("start", start), group=0)
|
||
application.add_handler(CommandHandler("help", help_command), group=0)
|
||
application.add_handler(CommandHandler("sync_groups", sync_groups_command), group=0)
|
||
|
||
# Добавляем обработчики callback'ов (группа 1)
|
||
application.add_handler(CallbackQueryHandler(start_callback, pattern=f"^{CallbackType.MAIN_MENU.value}$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(manage_messages, pattern=f"^{CallbackType.MANAGE_MESSAGES.value}$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(manage_groups, pattern=f"^{CallbackType.MANAGE_GROUPS.value}$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(list_messages, pattern=f"^{CallbackType.LIST_MESSAGES.value}$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(list_groups, pattern=f"^{CallbackType.LIST_GROUPS.value}$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(send_message, pattern=r"^send_msg_\d+$"), group=1)
|
||
# CREATE_MESSAGE обрабатывается отдельным handler'ом с управлением состоянием
|
||
application.add_handler(CallbackQueryHandler(create_message_start, pattern=f"^{CallbackType.CREATE_MESSAGE.value}$"), group=1)
|
||
# Добавляем обработчик CallbackQuery для управления UserBot
|
||
application.add_handler(CallbackQueryHandler(userbot_menu, pattern="^userbot_menu$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(userbot_settings, pattern="^userbot_settings$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(userbot_init, pattern="^userbot_init$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(userbot_collect_groups, pattern="^userbot_collect_groups$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(userbot_collect_members, pattern="^userbot_collect_members$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(userbot_parse_members, pattern=r"^userbot_members_\d+$"), group=1)
|
||
|
||
# Добавляем обработчик для кнопки UserBot в главном меню
|
||
application.add_handler(CallbackQueryHandler(userbot_menu, pattern=f"^{CallbackType.MANAGE_USERBOT.value}$"), group=1)
|
||
|
||
# Обработчики авторизации UserBot
|
||
application.add_handler(CallbackQueryHandler(auth_menu, pattern="^auth_menu$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(auth_info, pattern="^auth_info$"), group=1)
|
||
|
||
# ConversationHandler для полного процесса авторизации
|
||
auth_conversation = ConversationHandler(
|
||
entry_points=[
|
||
CallbackQueryHandler(start_phone_input, pattern="^auth_start_phone$"),
|
||
],
|
||
states={
|
||
2: [MessageHandler(filters.TEXT & ~filters.COMMAND, handle_phone)], # AUTH_PHONE = 2
|
||
3: [MessageHandler(filters.TEXT & ~filters.COMMAND, handle_code)], # AUTH_CODE = 3
|
||
4: [MessageHandler(filters.TEXT & ~filters.COMMAND, handle_password)], # AUTH_PASSWORD = 4
|
||
},
|
||
fallbacks=[
|
||
CallbackQueryHandler(cancel_auth, pattern="^cancel_auth$"),
|
||
CommandHandler("cancel", cancel_auth),
|
||
],
|
||
name="userbot_auth",
|
||
persistent=False
|
||
)
|
||
application.add_handler(auth_conversation, group=1)
|
||
|
||
# Select group callbacks
|
||
application.add_handler(CallbackQueryHandler(select_groups, pattern=r"^select_group_\d+$"), group=1)
|
||
application.add_handler(CallbackQueryHandler(select_groups, pattern=r"^done_groups$"), group=1)
|
||
|
||
# MessageHandler для текстового ввода (название и текст сообщения)
|
||
# Использует dispatch-функцию для маршрутизации в зависимости от состояния
|
||
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message_input), group=1)
|
||
|
||
# Обработчик добавления/удаления бота из групп (группа 3)
|
||
application.add_handler(ChatMemberHandler(my_chat_member, ChatMemberHandler.MY_CHAT_MEMBER), group=3)
|
||
|
||
# Запускаем бота
|
||
logger.info("🚀 Бот запущен. Ожидание команд...")
|
||
try:
|
||
await application.run_polling(allowed_updates=["message", "callback_query", "my_chat_member"])
|
||
finally:
|
||
# Завершить Telethon клиент при выходе
|
||
if Config.USE_TELETHON:
|
||
logger.info("Завершение работы Telethon клиента...")
|
||
await telethon_manager.shutdown()
|
||
logger.info("✅ Telethon клиент остановлен")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
import asyncio
|
||
asyncio.run(main())
|