Разрабатываю Telegram-бота на aiogram, который использует Telethon для парсинга сообщений, в том числе из приватных групп. Для доступа к приватным группам нужна авторизация пользователя. И вот тут проблема.
Суть проблемы:
Пользователь вводит номер, бот отправляет код (client.send_code_request). Пользователь вводит правильный код (из приложения Telegram, а не пересылая его), но авторизация не проходит. Бот пишет "Ошибка аутентификации", а в Telegram приходит такое сообщение:
Незавершённая попытка входа. [...]. Код был введен верно, но вход был заблокирован, поскольку ранее Вы сообщили этот код со своего аккаунта.
Важно:
aiogram + Telethon.
Код точно правильный.
Есть обработка ошибок: PhoneNumberInvalidError, PhoneNumberUnoccupiedError, FloodWaitError, SessionPasswordNeededError.
Задержка 10 секунд после send_code_request.
Блокировка при client.sign_in(code=code).
Что пробовал:
Добавлял задержки.
Удалял сессии.
Разные номера (с 2FA и без).
API ID/HASH верные.
Код:
from aiogram import types, Router, F
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import StatesGroup, State
from aiogram.utils.keyboard import InlineKeyboardBuilder
from decouple import config
from telethon import TelegramClient, errors
from telethon.sessions import StringSession
from telethon.errors import PhoneNumberInvalidError, PhoneNumberUnoccupiedError
import os
import asyncio
router = Router()
user_clients = {}
class MyAccountStates(StatesGroup):
waiting_for_phone = State()
waiting_for_code = State()
waiting_for_password = State()
router.callback_query(lambda c: c.data == "add_account")
async def add_account_handler(callback_query: types.CallbackQuery, state: FSMContext):
builder = InlineKeyboardBuilder()
builder.button(text="Отмена", callback_data="cancel_my_account")
await callback_query.message.edit_text(
"Введите номер (+71234567890):", reply_markup=builder.as_markup()
)
await state.set_state(MyAccountStates.waiting_for_phone)
router.message(MyAccountStates.waiting_for_phone)
async def process_phone(message: types.Message, state: FSMContext):
phone = message.text.strip()
if not phone.startswith('+'):
await message.reply("Номер с '+'.")
return
user_id = message.from_user.id
session_file = f"sessions/{user_id}.session"
if os.path.exists(session_file):
try:
client = TelegramClient(StringSession(open(session_file).read()), config('API_ID'), config('API_HASH'))
await client.connect()
if await client.is_user_authorized():
user_clients[user_id] = client
await message.reply("Уже подключен.")
await state.clear()
return
else:
os.remove(session_file)
except errors.AuthKeyUnregisteredError:
os.remove(session_file)
except Exception as e:
await message.reply(f"Ошибка сессии: {e}")
await state.clear()
return
client = TelegramClient(StringSession(), config('API_ID'), config('API_HASH'))
await client.connect()
try:
await client.send_code_request(phone)
await asyncio.sleep(10)
except (PhoneNumberInvalidError, PhoneNumberUnoccupiedError, errors.FloodWaitError) as e:
await message.reply(f"Ошибка: {e}")
await state.clear()
return
except Exception as e:
await message.reply(f"Ошибка кода: {e}")
await state.clear()
return
builder = InlineKeyboardBuilder()
builder.button(text="Отмена", callback_data="cancel_my_account")
await message.reply("Введите код:", reply_markup=builder.as_markup())
await state.set_state(MyAccountStates.waiting_for_code)
user_clients[user_id] = client
router.message(MyAccountStates.waiting_for_code)
async def process_code(message: types.Message, state: FSMContext):
code = message.text.strip()
user_id = message.from_user.id
if user_id not in user_clients:
await message.reply("Ошибка. Сначала.")
await state.clear()
return
client = user_clients[user_id]
try:
await client.sign_in(code=code)
except errors.SessionPasswordNeededError:
builder = InlineKeyboardBuilder()
builder.button(text="Отмена", callback_data="cancel_my_account")
await message.reply("Пароль 2FA:", reply_markup=builder.as_markup())
await state.set_state(MyAccountStates.waiting_for_password)
return
except errors.FloodWaitError as e:
await message.reply(f"Ждите {e.seconds} сек.")
await state.clear()
return
except Exception as e:
await message.reply(f"Ошибка: {e}")
await state.clear()
await client.disconnect()
return
session_string = client.session.save()
os.makedirs("sessions", exist_ok=True)
with open(f"sessions/{user_id}.session", "w") as f:
f.write(session_string)
await message.reply("Добавлен!")
await state.clear()
router.message(MyAccountStates.waiting_for_password)
async def process_password(message: types.Message, state: FSMContext):
password = message.text.strip()
user_id = message.from_user.id
if user_id not in user_clients:
await message.reply("Ошибка. Сначала.")
await state.clear()
return
client = user_clients[user_id]
try:
await client.sign_in(password=password)
except errors.FloodWaitError as e:
await message.reply(f"Ждите {e.seconds} сек.")
await state.clear()
return
except Exception as e:
await message.reply(f"Ошибка: {e}")
await state.clear()
await client.disconnect()
return
session_string = client.session.save()
os.makedirs("sessions", exist_ok=True)
with open(f"sessions/{user_id}.session", "w") as f:
f.write(session_string)
await message.reply("Добавлен!")
await state.clear()
router.callback_query(lambda c: c.data == "cancel_my_account")
async def cancel_my_account(callback_query: types.CallbackQuery, state: FSMContext):
user_id = callback_query.from_user.id
if user_id in user_clients:
client = user_clients[user_id]
await client.disconnect()
del user_clients[user_id]
await state.clear()
router.callback_query(lambda c: c.data == "remove_account")
async def remove_account_handler(callback_query: types.CallbackQuery, state: FSMContext):
user_id = callback_query.from_user.id
session_file = f"sessions/{user_id}.session"
if user_id in user_clients:
client = user_clients[user_id]
await client.disconnect()
del user_clients[user_id]
if os.path.exists(session_file):
os.remove(session_file)
await callback_query.answer("Удален.")
else:
await callback_query.answer("Не найден.")
await state.clear()
Кто-нибудь сталкивался с подобным? Есть идеи, как заставить Telethon авторизоваться? Может, какие-то настройки безопасности Telegram надо изменить? Или я что-то упускаю в коде?