Merge pull request #69 from oplexz/main

Implement login via QR-code in session_string_generator; bump Telethon to 1.42.0
This commit is contained in:
Eugene Evstafev 2026-03-05 08:15:15 +00:00 committed by GitHub
commit 26b1287a5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 130 additions and 49 deletions

View file

@ -29,7 +29,8 @@ dependencies = [
"nest-asyncio>=1.6.0", "nest-asyncio>=1.6.0",
"python-dotenv>=1.1.0", "python-dotenv>=1.1.0",
"python-json-logger>=3.3.0", "python-json-logger>=3.3.0",
"telethon>=1.39.0" "qrcode>=8.2",
"telethon>=1.42.0",
] ]
[project.urls] [project.urls]

View file

@ -4,4 +4,5 @@ mcp[cli]>=1.4.1
nest-asyncio>=1.6.0 nest-asyncio>=1.6.0
python-dotenv>=1.1.0 python-dotenv>=1.1.0
python-json-logger>=3.3.0 python-json-logger>=3.3.0
telethon>=1.39.0 qrcode>=8.2
telethon>=1.42.0

View file

@ -19,16 +19,76 @@ parameters support integer IDs, string representations of IDs (e.g., "123456"),
and usernames (e.g., "@mychannel"). and usernames (e.g., "@mychannel").
""" """
import asyncio
import io
import os import os
from telethon.sync import TelegramClient
from telethon.sessions import StringSession
from dotenv import load_dotenv
import sys import sys
# Load environment variables from .env file from dotenv import load_dotenv
from telethon import errors
from telethon.sessions import StringSession
from telethon.sync import TelegramClient
load_dotenv() load_dotenv()
def _qr_login(client: TelegramClient) -> None:
import qrcode
qr = client.qr_login()
print("\n----- QR Code Login -----\n")
qr_obj = qrcode.QRCode(border=1)
qr_obj.add_data(qr.url)
qr_obj.make(fit=True)
f = io.StringIO()
qr_obj.print_ascii(out=f, invert=True)
print(f.getvalue())
print("Scan the QR code above with your Telegram app:")
print(" Open Telegram > Settings > Devices > Link Desktop Device\n")
print(f"Or open this link on a device where you're logged in:\n {qr.url}\n")
print(f"Expires at: {qr.expires.strftime('%H:%M:%S')}")
print("Waiting for you to scan...")
try:
client.loop.run_until_complete(qr.wait(timeout=120))
except asyncio.TimeoutError:
print("\nQR code expired. Please try again.")
client.disconnect()
sys.exit(1)
except errors.SessionPasswordNeededError:
pw = input("\nTwo-factor authentication enabled. Please enter your password: ")
client.sign_in(password=pw)
def _phone_login(client: TelegramClient) -> None:
phone = input("Please enter your phone (or bot token): ")
try:
client.send_code_request(phone)
except errors.FloodWaitError as e:
print(f"\nFlood wait error; you must wait {e.seconds} seconds before trying again.")
client.disconnect()
sys.exit(1)
except errors.PhoneNumberInvalidError:
print("\nThe phone number is invalid.")
client.disconnect()
sys.exit(1)
except Exception as e:
print(f"\nError sending code: {e}")
client.disconnect()
sys.exit(1)
code = input("\nPlease enter the code you received: ")
try:
client.sign_in(phone, code)
except errors.SessionPasswordNeededError:
pw = input("Two-factor authentication enabled. Please enter your password: ")
client.sign_in(password=pw)
def main() -> None: def main() -> None:
API_ID = os.getenv("TELEGRAM_API_ID") API_ID = os.getenv("TELEGRAM_API_ID")
API_HASH = os.getenv("TELEGRAM_API_HASH") API_HASH = os.getenv("TELEGRAM_API_HASH")
@ -38,7 +98,6 @@ def main() -> None:
print("Create an .env file with your credentials from https://my.telegram.org/apps") print("Create an .env file with your credentials from https://my.telegram.org/apps")
sys.exit(1) sys.exit(1)
# Convert API_ID to integer
try: try:
API_ID = int(API_ID) API_ID = int(API_ID)
except ValueError: except ValueError:
@ -47,56 +106,62 @@ def main() -> None:
print("\n----- Telegram Session String Generator -----\n") print("\n----- Telegram Session String Generator -----\n")
print("This script will generate a session string for your Telegram account.") print("This script will generate a session string for your Telegram account.")
print(
"You will be asked to enter your phone number and the verification code sent to your Telegram app."
)
print("The generated session string can be added to your .env file.") print("The generated session string can be added to your .env file.")
print( print(
"\nYour credentials will NOT be stored on any server and are only used for local authentication.\n" "\nYour credentials will NOT be stored on any server and are only used for local authentication.\n"
) )
print("Choose login method:")
print(" 1) QR code login (recommended -- scan from your Telegram app)")
print(" 2) Phone number + verification code")
method = input("\nEnter 1 or 2 [default: 1]: ").strip() or "1"
try: try:
# Connect to Telegram and generate the session string client = TelegramClient(StringSession(), API_ID, API_HASH)
with TelegramClient(StringSession(), API_ID, API_HASH) as client: client.connect()
# The client.session.save() function from StringSession returns the session string
session_string = StringSession.save(client.session)
print("\nAuthentication successful!") if not client.is_user_authorized():
print("\n----- Your Session String -----") if method == "1":
print(f"\n{session_string}\n") _qr_login(client)
print("Add this to your .env file as:") else:
print(f"TELEGRAM_SESSION_STRING={session_string}") _phone_login(client)
print("\nIMPORTANT: Keep this string private and never share it with anyone!")
# Optional: auto-update the .env file session_string = StringSession.save(client.session)
choice = input(
"\nWould you like to automatically update your .env file with this session string? (y/N): "
)
if choice.lower() == "y":
try:
# Read the current .env file
with open(".env", "r") as file:
env_contents = file.readlines()
# Update or add the SESSION_STRING line print("\nAuthentication successful!")
session_string_line_found = False print("\n----- Your Session String -----")
for i, line in enumerate(env_contents): print(f"\n{session_string}\n")
if line.startswith("TELEGRAM_SESSION_STRING="): print("Add this to your .env file as:")
env_contents[i] = f"TELEGRAM_SESSION_STRING={session_string}\n" print(f"TELEGRAM_SESSION_STRING={session_string}")
session_string_line_found = True print("\nIMPORTANT: Keep this string private and never share it with anyone!")
break
if not session_string_line_found: choice = input(
env_contents.append(f"TELEGRAM_SESSION_STRING={session_string}\n") "\nWould you like to automatically update your .env file with this session string? (y/N): "
)
if choice.lower() == "y":
try:
with open(".env", "r") as file:
env_contents = file.readlines()
# Write back to the .env file session_string_line_found = False
with open(".env", "w") as file: for i, line in enumerate(env_contents):
file.writelines(env_contents) if line.startswith("TELEGRAM_SESSION_STRING="):
env_contents[i] = f"TELEGRAM_SESSION_STRING={session_string}\n"
session_string_line_found = True
break
print("\n.env file updated successfully!") if not session_string_line_found:
except Exception as e: env_contents.append(f"TELEGRAM_SESSION_STRING={session_string}\n")
print(f"\nError updating .env file: {e}")
print("Please manually add the session string to your .env file.") with open(".env", "w") as file:
file.writelines(env_contents)
print("\n.env file updated successfully!")
except Exception as e:
print(f"\nError updating .env file: {e}")
print("Please manually add the session string to your .env file.")
client.disconnect()
except Exception as e: except Exception as e:
print(f"\nError: {e}") print(f"\nError: {e}")

22
uv.lock
View file

@ -825,6 +825,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" },
] ]
[[package]]
name = "qrcode"
version = "8.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8f/b2/7fc2931bfae0af02d5f53b174e9cf701adbb35f39d69c2af63d4a39f81a9/qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c", size = 43317, upload-time = "2025-05-01T15:44:24.726Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" },
]
[[package]] [[package]]
name = "referencing" name = "referencing"
version = "0.37.0" version = "0.37.0"
@ -1041,6 +1053,7 @@ dependencies = [
{ name = "nest-asyncio" }, { name = "nest-asyncio" },
{ name = "python-dotenv" }, { name = "python-dotenv" },
{ name = "python-json-logger" }, { name = "python-json-logger" },
{ name = "qrcode" },
{ name = "telethon" }, { name = "telethon" },
] ]
@ -1060,7 +1073,8 @@ requires-dist = [
{ name = "nest-asyncio", specifier = ">=1.6.0" }, { name = "nest-asyncio", specifier = ">=1.6.0" },
{ name = "python-dotenv", specifier = ">=1.1.0" }, { name = "python-dotenv", specifier = ">=1.1.0" },
{ name = "python-json-logger", specifier = ">=3.3.0" }, { name = "python-json-logger", specifier = ">=3.3.0" },
{ name = "telethon", specifier = ">=1.39.0" }, { name = "qrcode", specifier = ">=8.2" },
{ name = "telethon", specifier = ">=1.42.0" },
] ]
[package.metadata.requires-dev] [package.metadata.requires-dev]
@ -1073,15 +1087,15 @@ dev = [
[[package]] [[package]]
name = "telethon" name = "telethon"
version = "1.39.0" version = "1.42.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "pyaes" }, { name = "pyaes" },
{ name = "rsa" }, { name = "rsa" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/54/b2/6e48b593a0e4e678578a682332ef36e54648c78ef13a5d395e5cdd293c6d/telethon-1.39.0.tar.gz", hash = "sha256:35d4795d8c91deac515fb0bcb3723866b924de1c724e1d5c230460e96f284a63", size = 640634, upload-time = "2025-02-20T17:30:44.562Z" } sdist = { url = "https://files.pythonhosted.org/packages/8c/10/8c8c9476bfce767a856d8aaf9eae8ea1869df4e970da16f1c5b638fd1b0c/telethon-1.42.0.tar.gz", hash = "sha256:032e95511261d5ead719f75494c6c85ece2ce71816b54f3c65d6ccc371d6994d", size = 672734, upload-time = "2025-11-05T19:15:19.849Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/32/f2/4e66a59c6d0cb6a25c0c2a630b4b184d81cfe8cb982bf3b65ce32b020fc8/Telethon-1.39.0-py3-none-any.whl", hash = "sha256:aa9f394b94be144799a6f6a93ab463867bc7c63503ede9631751940a98f6c703", size = 715851, upload-time = "2025-02-20T17:30:41.955Z" }, { url = "https://files.pythonhosted.org/packages/e4/e4/8ce0ff55251381966a7c3f88bd5b34abda79b225a8e7fb51ddef3b849c94/telethon-1.42.0-py3-none-any.whl", hash = "sha256:cf361c94586bcacd6d0fc8959a2bce509d1bb37007fe6476a80c4fb4a2decc29", size = 748466, upload-time = "2025-11-05T19:15:18.241Z" },
] ]
[[package]] [[package]]