🔐 Добавлена полная поддержка 2FA авторизации

 Улучшения:
   Расширенная обработка ошибок при вводе пароля 2FA
   Различие между неверным паролем и другими ошибками
   Подробные подсказки для пользователя при ошибках
   Поддержка восстановительных кодов 2FA
   Улучшенное сообщение при запросе пароля 2FA

📖 Документация:
   Создан 2FA_GUIDE.md (подробное руководство)
   Обновлена информация о 2FA в боте (auth_info)
   Добавлены примеры и советы по использованию

🔐 Обработка ошибок:
  • Неверный пароль - ясное сообщение + подсказки
  • Пароль истек - предложение повторить
  • SMS-код истек - инструкция по получению нового
  • Много попыток - информация о ограничениях

📱 Процесс с 2FA:
  1. Номер телефона
  2. SMS-код (5 цифр)
  3. Пароль 2FA (если включена)
  4.  Авторизация успешна

💡 Основные преимущества:
  • Ясные объяснения на каждом этапе
  • Подсказки при забывании пароля
  • Безопасное обращение с паролями (не сохраняются)
  • Поддержка восстановительных кодов
This commit is contained in:
2025-12-21 12:33:29 +09:00
parent b4f86a33cb
commit c849866fbd
7 changed files with 308 additions and 66 deletions

View File

@@ -142,43 +142,25 @@ async def main() -> None:
# Обработчики авторизации UserBot
application.add_handler(CallbackQueryHandler(auth_menu, pattern="^auth_menu$"), group=1)
application.add_handler(CallbackQueryHandler(auth_info, pattern="^auth_info$"), group=1)
application.add_handler(CallbackQueryHandler(start_phone_input, pattern="^auth_start_phone$"), group=1)
# Конверсейшн для ввода номера телефона
auth_phone_conversation = ConversationHandler(
entry_points=[], # Входная точка через callback query выше
# 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
},
fallbacks=[CallbackQueryHandler(cancel_auth, pattern="^cancel_auth$")],
name="auth_phone",
persistent=False
)
application.add_handler(auth_phone_conversation, group=1)
# Конверсейшн для ввода SMS кода
auth_code_conversation = ConversationHandler(
entry_points=[], # Входная точка через callback query выше
states={
3: [MessageHandler(filters.TEXT & ~filters.COMMAND, handle_code)], # AUTH_CODE = 3
},
fallbacks=[CallbackQueryHandler(cancel_auth, pattern="^cancel_auth$")],
name="auth_code",
persistent=False
)
application.add_handler(auth_code_conversation, group=1)
# Конверсейшн для ввода пароля 2FA
auth_password_conversation = ConversationHandler(
entry_points=[], # Входная точка через callback query выше
states={
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$")],
name="auth_password",
fallbacks=[
CallbackQueryHandler(cancel_auth, pattern="^cancel_auth$"),
CommandHandler("cancel", cancel_auth),
],
name="userbot_auth",
persistent=False
)
application.add_handler(auth_password_conversation, group=1)
application.add_handler(auth_conversation, group=1)
# Select group callbacks
application.add_handler(CallbackQueryHandler(select_groups, pattern=r"^select_group_\d+$"), group=1)

View File

@@ -87,14 +87,26 @@ async def auth_info(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
<b>Как это работает:</b>
1. Введите номер телефона вашего аккаунта Telegram
2. Получите SMS-код подтверждения
2. Получите SMS-код подтверждения (5 цифр)
3. Введите код в бот
4. При необходимости подтвердите двухфакторную аутентификацию
4. Если включена 2FA - введите пароль
5. Готово! UserBot авторизован
<b>Что такое SMS-код?</b>
Это 5-значный код, который Telegram отправляет на ваш номер.
Действителен ~5 минут. Нужно ввести быстро.
<b>Что такое 2FA пароль?</b>
Это пароль, который ВЫ установили в Telegram на случай потери телефона.
<i>НЕ SMS-код, НЕ пароль от почты!</i>
📍 Где его найти?
Telegram → Настройки → Приватность и безопасность → Двухфакторная аутентификация
<b>Безопасность:</b>
✓ Авторизация происходит локально на сервере
Ваш пароль не передается нигде
Пароли НЕ сохраняются в базе (обработаны Telethon)
✓ SMS-коды НЕ логируются
✓ Сессия сохраняется в зашифрованном виде
✓ Доступ имеет только этот бот
@@ -105,10 +117,18 @@ async def auth_info(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
✓ Работать 24/7 без вашего участия
<b>Что НЕ может делать:</b>
✗ Отправлять сообщения от вашего имени (опционально)
✗ Отправлять сообщения от вашего имени
✗ Удалять или изменять ваши сообщения
✗ Изменять настройки групп
✗ Получать доступ к приватным чатам других пользователей
<b>Справка по ошибкам:</b>
"Неверный номер" - проверьте формат (+XX...)
"Код истек" - повторите авторизацию
"Требуется пароль" - введите пароль 2FA
"Неверный пароль" - проверьте регистр и опечатки
📖 Полное руководство: смотрите 2FA_GUIDE.md
"""
keyboard = [
@@ -333,9 +353,20 @@ async def handle_code(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int
except SessionPasswordNeededError:
# Нужна двухфакторная аутентификация
await message.edit_text(
"""🔐 <b>Требуется двухфакторная аутентификация</b>
"""🔐 <b>Требуется двухфакторная аутентификация (2FA)</b>
Введите пароль вашего аккаунта Telegram:
Ваш аккаунт Telegram защищен паролем 2FA.
<b>Что вводить:</b>
Введите пароль, который ВЫ установили в Telegram
(это НЕ SMS-код и НЕ пароль от почты)
📍 Как найти:
Telegram → Настройки → Приватность и безопасность → Двухфакторная аутентификация
<i>Пароль чувствителен к регистру!</i>
Введите пароль:
""",
parse_mode='HTML'
)
@@ -375,50 +406,87 @@ async def handle_password(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
client = auth_sessions[user_id]['client']
# Проверяем пароль
await client.sign_in(password=password)
try:
# Пытаемся войти с пароль
await client.sign_in(password=password)
# Успешная авторизация
await message.edit_text(
"""✅ <b>Двухфакторная аутентификация успешна!</b>
# Успешная авторизация
await message.edit_text(
"""✅ <b>Двухфакторная аутентификация успешна!</b>
Ваш UserBot авторизован и готов к работе.
""",
parse_mode='HTML'
)
# Сохраняем правильное имя сессии
correct_session = f"app/sessions/userbot_{user_id}"
if os.path.exists(f"app/sessions/userbot_auth_{user_id}.session"):
os.rename(
f"app/sessions/userbot_auth_{user_id}.session",
f"{correct_session}.session"
Сессия сохранена на сервере. Переавторизация не требуется.
""",
parse_mode='HTML'
)
await client.disconnect()
# Очищаем временные данные
if user_id in auth_sessions:
del auth_sessions[user_id]
# Сохраняем правильное имя сессии
correct_session = f"app/sessions/userbot_{user_id}"
if os.path.exists(f"app/sessions/userbot_auth_{user_id}.session"):
os.rename(
f"app/sessions/userbot_auth_{user_id}.session",
f"{correct_session}.session"
)
keyboard = [
[InlineKeyboardButton("✅ Готово", callback_data="userbot_menu")],
]
await message.reply_text(
"Нажмите кнопку, чтобы вернуться в меню UserBot.",
reply_markup=InlineKeyboardMarkup(keyboard)
)
await client.disconnect()
# Очищаем временные данные
if user_id in auth_sessions:
del auth_sessions[user_id]
return ConversationHandler.END
keyboard = [
[InlineKeyboardButton("✅ Готово", callback_data="userbot_menu")],
]
await message.reply_text(
"Нажмите кнопку, чтобы вернуться в меню UserBot.",
reply_markup=InlineKeyboardMarkup(keyboard)
)
return ConversationHandler.END
except Exception as password_error:
error_msg = str(password_error).lower()
# Проверяем тип ошибки
if "password" in error_msg or "invalid" in error_msg:
await message.edit_text(
"""❌ <b>Неверный пароль</b>
Пароль, который вы ввели, неправильный.
💡 Подсказки:
• Убедитесь, что пароль введен без опечаток
• Пароль должен совпадать с тем, который вы установили в Telegram
• Учитывается регистр букв
• Если забыли пароль, используйте восстановительный код
Попробуйте еще раз или используйте восстановительный код:
<code>код_восстановления</code>
""",
parse_mode='HTML'
)
else:
await message.edit_text(
f"""❌ <b>Ошибка при проверке пароля</b>
{error_msg[:100]}
Пожалуйста, попробуйте еще раз или начните авторизацию заново.
""",
parse_mode='HTML'
)
return AUTH_PASSWORD
except Exception as e:
logger.error(f"Password verification error: {e}")
await message.edit_text(
f"""❌ <b>Ошибка при проверке пароля</b>
f"""❌ <b>Критическая ошибка</b>
{str(e)}
{str(e)[:150]}
Пожалуйста, попробуйте еще раз.
Пожалуйста, начните авторизацию заново.
""",
parse_mode='HTML'
)

Binary file not shown.