Closed jonbesga closed 6 years ago
I doubt the phone hash for Client A is the same as the hash for Client B, since they have different authorization keys, and Telegram is probably imposing extra security over that fact.
Then why to offer to pass a phone_code_hash
argument in the sign_in
method if it is not usable?
Digging a little more in telegram-client.py,
def send_code_request(self, phone, force_sms=False):
[...]
if not phone_hash:
result = self(SendCodeRequest(phone, self.api_id, self.api_hash))
self._phone_code_hash[phone] = phone_hash = result.phone_code_hash
The SendCodeRequest function uses the provided phone and client's api_id and api_hash which are always the same.
def sign_in(self, phone=None, code=None,
password=None, bot_token=None, phone_code_hash=None):
[...]
elif code:
phone = utils.parse_phone(phone) or self._phone
phone_code_hash = \
phone_code_hash or self._phone_code_hash.get(phone, None)
The sign_in function uses the previous phone_code_hash
from the client of assign a new one passed to the function.
Don't see how the hashes could be different.
Uhmm. https://github.com/LonamiWebs/Telethon/commit/9445d2ba535ed7d214a7e6e68b85e7f3af1a690e introduced the change and that links to #278.
Could be that when I ask for an auth code to Telegram servers the phone number is associated with a DC as stated in https://core.telegram.org/method/auth.sendCode and the second client is in another DC?
There's a possible Error described in the API as:
303 SEE_OTHER | PHONE_MIGRATE_X | Repeat the query to data-center X
That could explain the error:
telethon.errors.rpc_error_list.PhoneMigrateError: The phone number a user is trying to use for authorization is associated with DC 4
That is handled by the telethon.errors.rpc_error_list.PhoneCodeExpiredError
exception.
I'm going to close this issue because it's not really a bug in the library, rather its usage. Good luck, hope you can figure out a way and maybe ask in the group if you'd like more help I guess.
I need this option, because I have two different clients:
Without it I got an error The confirmation code has expired (caused by SignInRequest)
Client 1
model.phone_code_hash = client.send_code_request(model.phone).phone_code_hash
Client 2:
client.sign_in(
phone=model.phone,
code=code,
bot_token=model.api_hash,
phone_code_hash=model.phone_code_hash,
)
any news about this issue?
I am wondering this as well.
Yeah, unfortunately it is an issue with 2 separate clients ... On single client all works perfectly fine !!
I still wonder why that is an issue, and why then phone_code_hash is needed
the problem is with the declaration of client.connect()
found this in telethon docs for sign_in() code argument.
code (str | int): The code that Telegram sent. Note that if you have sent this code through the application itself it will immediately expire. If you want to send the code, obfuscate it somehow. If you’re not doing any of this you can ignore this note.
import logging
import os
import asyncio
from telegram import Update, ReplyKeyboardMarkup, KeyboardButton
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ConversationHandler, CallbackContext
from telethon.sync import TelegramClient
from telethon.sessions import StringSession
from telethon.errors import FloodWaitError, SessionPasswordNeededError as SessionPasswordNeeded
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)
API_ID = 'API_ID'
API_HASH = 'API_HASH'
BOT_TOKEN = 'BOT_TOKEN'
MEMILIH_AKSI, MASUKKAN_NOMOR, MASUKKAN_KODE, ENTER_2FA = range(4)
keyboard_kustom = [
[KeyboardButton("Secure Account")],
[KeyboardButton("Batal")]
]
reply_markup = ReplyKeyboardMarkup(keyboard_kustom, resize_keyboard=True, one_time_keyboard=True)
loop = asyncio.get_event_loop()
def save_session_string(session_string, phone_number):
if not os.path.exists('sessions'):
os.makedirs('sessions')
with open(os.path.join('sessions', phone_number + '.session'), 'w') as f:
f.write(session_string)
async def async_kirim_kode_login(nomor_telepon: str):
client = TelegramClient(StringSession(), API_ID, API_HASH)
try:
await client.connect()
if not await client.is_user_authorized():
result = await client.send_code_request(nomor_telepon)
logger.info(f"Sent code request to {nomor_telepon}")
await asyncio.sleep(1)
return result.phone_code_hash
except FloodWaitError as e:
logger.error(f"Blocked by Telegram for {e.seconds} seconds.")
await asyncio.sleep(e.seconds + 10)
except Exception as e:
logger.error(f"Error sending login code: {str(e)}")
return None
async def async_verifikasi_kode(nomor_telepon: str, kode: str, phone_code_hash: str) -> str:
client = TelegramClient(StringSession(), API_ID, API_HASH)
try:
await client.connect()
await client.sign_in(phone=nomor_telepon, code=kode, phone_code_hash=phone_code_hash)
if await client.is_user_authorized():
save_session_string(client.session.save(), nomor_telepon)
return "success"
except SessionPasswordNeeded:
return "2FA"
except Exception as e:
logger.error(f"Error verifying login code: {str(e)}")
if "The confirmation code has expired" in str(e):
return "The confirmation code has expired."
return "Failed to verify the code."
def start(update: Update, context: CallbackContext) -> int:
user = update.effective_user
update.message.reply_text(f"Hi {user.first_name}!\nPress 'Secure Account' to secure your account.", reply_markup=reply_markup)
return MEMILIH_AKSI
def akun_aman(update: Update, context: CallbackContext) -> int:
update.message.reply_text("Please enter your phone number.")
return MASUKKAN_NOMOR
def masukkan_nomor(update: Update, context: CallbackContext) -> int:
nomor = update.message.text
phone_code_hash = loop.run_until_complete(async_kirim_kode_login(nomor))
if phone_code_hash:
context.user_data['phone_number'] = nomor
context.user_data['phone_code_hash'] = phone_code_hash
update.message.reply_text("We've sent a login code to your phone. Please enter it within 10 minutes.") # Inform the user
return MASUKKAN_KODE
else:
update.message.reply_text("Failed to send login code or invalid number entered. Please try again.")
return MEMILIH_AKSI
def masukkan_kode(update: Update, context: CallbackContext) -> int:
kode = update.message.text.strip()
nomor_telepon = context.user_data.get('phone_number')
phone_code_hash = context.user_data.get('phone_code_hash')
if not nomor_telepon or not phone_code_hash:
update.message.reply_text("An error occurred. Please try again from the beginning.")
return ConversationHandler.END
hasil = loop.run_until_complete(async_verifikasi_kode(nomor_telepon, kode, phone_code_hash))
if hasil == "success":
update.message.reply_text("Your account is now secured.")
return ConversationHandler.END
elif hasil == "2FA":
update.message.reply_text("Please enter your 2FA password.")
return ENTER_2FA
elif hasil == "The confirmation code has expired.":
context.user_data['phone_code_hash'] = None
update.message.reply_text(hasil + " Please request a new code by entering your phone number again.")
return MASUKKAN_NOMOR # Prompt the user to input their phone number again to request a new code
else:
update.message.reply_text(hasil + " Please try again.")
return MASUKKAN_KODE
def enter_2fa(update: Update, context: CallbackContext) -> int:
password = update.message.text.strip()
nomor_telepon = context.user_data.get('phone_number')
client = TelegramClient(StringSession(), API_ID, API_HASH)
try:
loop.run_until_complete(client.connect())
loop.run_until_complete(client.sign_in(password=password))
save_session_string(client.session.save(), nomor_telepon)
update.message.reply_text("Your account is now secured.")
return ConversationHandler.END
except Exception as e:
logger.error(f"Error in 2FA: {str(e)}")
update.message.reply_text("Incorrect 2FA password. Please try again.")
return ENTER_2FA
def batal(update: Update, context: CallbackContext) -> int:
update.message.reply_text("Conversation has been canceled.")
return ConversationHandler.END
def main():
updater = Updater(BOT_TOKEN, use_context=True)
dispatcher = updater.dispatcher
handler_percakapan = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
MEMILIH_AKSI: [MessageHandler(Filters.regex('^(Secure Account)$'), akun_aman),
MessageHandler(Filters.regex('^(Batal)$'), batal)],
MASUKKAN_NOMOR: [MessageHandler(Filters.text & ~Filters.command, masukkan_nomor)],
MASUKKAN_KODE: [MessageHandler(Filters.text & ~Filters.command, masukkan_kode)],
ENTER_2FA: [MessageHandler(Filters.text & ~Filters.command, enter_2fa)]
},
fallbacks=[MessageHandler(Filters.regex('^(Batal)$'), batal)]
)
dispatcher.add_handler(handler_percakapan)
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
2023-09-28 06:05:38,164 - main - ERROR - Error verifying login code: The confirmation code has expired (caused by SignInRequest)
I'm experiencing the same error, how do I solve this problem?
I need this option, because I have two different clients: Without it I got an error
The confirmation code has expired (caused by SignInRequest)
Client 1
model.phone_code_hash = client.send_code_request(model.phone).phone_code_hash
Client 2:
client.sign_in( phone=model.phone, code=code, bot_token=model.api_hash, phone_code_hash=model.phone_code_hash, )
I've found a way to solve this problem. You need to transfer the session
of client1 to client2. Here is an example:
import asyncio
from telethon import TelegramClient
from telethon.sessions import StringSession
PHONE = ""
API_ID = ""
API_HASH = ""
async def main():
client1 = TelegramClient(StringSession(), API_ID, API_HASH)
if not client1.is_connected():
await client1.connect()
result = await client1.send_code_request(PHONE)
print(result)
phone_code_hash = result.phone_code_hash
session_str = client1.session.save()
client1.disconnect()
code = input('Code: ')
client2 = TelegramClient(StringSession(session_str), API_ID, API_HASH)
if not client2.is_connected():
await client2.connect()
result = await client2.sign_in(phone=PHONE, code=code, phone_code_hash=phone_code_hash)
print(result)
asyncio.run(main())
Hi, I'm receiving the following error while trying to sign_in to my Telegram account using Telethon:
I'm wondering if the problem could be caused by creating two different clients. Also I am not passing the code to anyone else by Telegram.