-
Notifications
You must be signed in to change notification settings - Fork 329
Expand file tree
/
Copy pathsession_string_generator.py
More file actions
executable file
·248 lines (199 loc) · 7.83 KB
/
Copy pathsession_string_generator.py
File metadata and controls
executable file
·248 lines (199 loc) · 7.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#!/usr/bin/env python3
"""
Telegram Session String Generator
This script generates a session string that can be used for Telegram authentication
with the Telegram MCP server. The session string allows for portable authentication
without storing session files.
Usage:
python session_string_generator.py
python session_string_generator.py --qr
Requirements:
- telethon
- python-dotenv
Note on ID Formats:
When using the MCP server, please be aware that all `chat_id` and `user_id`
parameters support integer IDs, string representations of IDs (e.g., "123456"),
and usernames (e.g., "@mychannel").
"""
import argparse
import asyncio
import getpass
import io
import os
import sys
from datetime import datetime, timezone
from dotenv import load_dotenv
from telethon import errors
from telethon.sessions import StringSession
from telethon.sync import TelegramClient
from telegram_mcp.client_identity import client_identity_kwargs
from telegram_mcp.install_guard import UnsafeInstallationError, assert_safe_distribution
# How many times the QR code is regenerated after expiry before giving up.
_QR_MAX_REFRESHES = 10
load_dotenv()
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Generate a Telegram session string for telegram-mcp."
)
login_group = parser.add_mutually_exclusive_group()
login_group.add_argument(
"--qr",
action="store_true",
help="Use Telegram QR login without prompting for a login method.",
)
login_group.add_argument(
"--phone",
action="store_true",
help="Use phone number + verification code login without prompting for a login method.",
)
return parser.parse_args()
def _check_installation() -> None:
try:
assert_safe_distribution()
except UnsafeInstallationError as exc:
print(str(exc), file=sys.stderr)
sys.exit(1)
def _render_qr(qr) -> None:
import qrcode
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...")
def _seconds_until_expiry(qr) -> float:
"""Seconds left before this QR token expires, with a small safety margin."""
expires = qr.expires
if expires.tzinfo is None:
expires = expires.replace(tzinfo=timezone.utc)
remaining = (expires - datetime.now(timezone.utc)).total_seconds()
return max(1.0, remaining - 1.0)
def _qr_login(client: TelegramClient) -> None:
qr = client.qr_login()
_render_qr(qr)
for _ in range(_QR_MAX_REFRESHES):
try:
client.loop.run_until_complete(qr.wait(timeout=_seconds_until_expiry(qr)))
return
except asyncio.TimeoutError:
client.loop.run_until_complete(qr.recreate())
print("\nQR code expired, here is a fresh one.")
_render_qr(qr)
except errors.SessionPasswordNeededError:
pw = getpass.getpass(
"\nTwo-factor authentication enabled. Please enter your password: "
)
client.sign_in(password=pw)
return
print("\nQR code expired too many times. Please run the generator again.")
client.disconnect()
sys.exit(1)
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 = getpass.getpass("Two-factor authentication enabled. Please enter your password: ")
client.sign_in(password=pw)
def main() -> None:
args = _parse_args()
_check_installation()
API_ID = os.getenv("TELEGRAM_API_ID")
API_HASH = os.getenv("TELEGRAM_API_HASH")
if not API_ID or not API_HASH:
print("Error: TELEGRAM_API_ID and TELEGRAM_API_HASH must be set in .env file")
print("Create an .env file with your credentials from https://my.telegram.org/apps")
sys.exit(1)
try:
API_ID = int(API_ID)
except ValueError:
print("Error: TELEGRAM_API_ID must be an integer")
sys.exit(1)
print("\n----- Telegram Session String Generator -----\n")
print("This script will generate a session string for your Telegram account.")
print("The generated session string can be added to your .env file.")
print(
"\nYour credentials will NOT be stored on any server and are only used for local authentication.\n"
)
label = (
input("Account label (optional, e.g. 'work', 'personal'; leave empty for default): ")
.strip()
.lower()
)
if args.qr:
method = "1"
elif args.phone:
method = "2"
else:
print("\nChoose 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:
client = TelegramClient(StringSession(), API_ID, API_HASH, **client_identity_kwargs())
client.connect()
if not client.is_user_authorized():
if method == "1":
_qr_login(client)
else:
_phone_login(client)
session_string = StringSession.save(client.session)
if label:
env_var = f"TELEGRAM_SESSION_STRING_{label.upper()}"
else:
env_var = "TELEGRAM_SESSION_STRING"
print("\nAuthentication successful!")
print("\n----- Your Session String -----")
print(f"\n{session_string}\n")
print("Add this to your .env file as:")
print(f"{env_var}={session_string}")
print("\nIMPORTANT: Keep this string private and never share it with anyone!")
choice = input(
"\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()
session_string_line_found = False
for i, line in enumerate(env_contents):
if line.startswith(f"{env_var}="):
env_contents[i] = f"{env_var}={session_string}\n"
session_string_line_found = True
break
if not session_string_line_found:
env_contents.append(f"{env_var}={session_string}\n")
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:
print(f"\nError: {e}")
print("Failed to generate session string. Please try again.")
sys.exit(1)
if __name__ == "__main__":
main()