feat: tg/ws/security/
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
tglib_to_tdata.py — Convert tglib.js JSON payload to a Telegram Desktop tdata zip.
|
||||
|
||||
Usage:
|
||||
python tglib_to_tdata.py <input.json> <output.zip>
|
||||
|
||||
Reads the tglib.js JSON (state + db_sqlite), extracts the master MTProto auth
|
||||
key + DC id, builds a Telethon SQLite session, and uses opentele-ng to write a
|
||||
tdata folder, then zips it.
|
||||
|
||||
Works fully offline — no Telegram connection is made.
|
||||
"""
|
||||
import json, base64, os, sys, tempfile, shutil, sqlite3, zipfile, asyncio
|
||||
|
||||
# Standard Telegram production DC endpoints (used to seed the Telethon session).
|
||||
DC_ADDRS = {
|
||||
1: ("149.154.175.50", 443),
|
||||
2: ("149.154.167.51", 443),
|
||||
3: ("149.154.175.100", 443),
|
||||
4: ("149.154.167.91", 443),
|
||||
5: ("91.108.56.130", 443),
|
||||
}
|
||||
|
||||
|
||||
def extract_keys(payload: dict):
|
||||
"""Extract master auth_key, dc_id, user_id from tglib.js JSON payload."""
|
||||
state_b64 = payload.get("state")
|
||||
if not state_b64:
|
||||
raise ValueError("missing 'state' field")
|
||||
state = json.loads(base64.b64decode(state_b64))
|
||||
records = state.get("records", [])
|
||||
if not records:
|
||||
raise ValueError("no records in state")
|
||||
backup_b64 = None
|
||||
for attr in records[0].get("attributes", []):
|
||||
if isinstance(attr, dict) and "backupData" in attr:
|
||||
backup_b64 = attr["backupData"]["data"]
|
||||
break
|
||||
if not backup_b64:
|
||||
raise ValueError("no backupData in state records")
|
||||
backup = json.loads(base64.b64decode(backup_b64))
|
||||
auth_key = base64.b64decode(backup["masterDatacenterKey"])
|
||||
dc_id = backup["masterDatacenterId"]
|
||||
user_id = backup.get("peerId", 0)
|
||||
if len(auth_key) != 256:
|
||||
raise ValueError(f"auth_key must be 256 bytes, got {len(auth_key)}")
|
||||
return auth_key, dc_id, user_id
|
||||
|
||||
|
||||
def make_session(tmpdir: str, auth_key: bytes, dc_id: int) -> str:
|
||||
"""Create a Telethon SQLite session file with the given auth key + DC."""
|
||||
from telethon.sessions import SQLiteSession
|
||||
server, port = DC_ADDRS.get(dc_id, ("149.154.167.91", 443))
|
||||
path = os.path.join(tmpdir, "tg")
|
||||
sess = SQLiteSession(path)
|
||||
sess._conn.execute("DELETE FROM sessions")
|
||||
sess._conn.execute(
|
||||
"INSERT INTO sessions (dc_id, server_address, port, auth_key) VALUES (?,?,?,?)",
|
||||
(dc_id, server, port, auth_key),
|
||||
)
|
||||
sess._conn.commit()
|
||||
sess.close()
|
||||
return path + ".session"
|
||||
|
||||
|
||||
def convert_to_tdata(session_file: str, out_dir: str):
|
||||
"""Use opentele-ng to convert Telethon session → tdata folder (offline)."""
|
||||
from opentele.td import TDesktop
|
||||
from opentele.tl import TelegramClient
|
||||
from opentele.api import UseCurrentSession
|
||||
|
||||
async def _run():
|
||||
client = TelegramClient(session_file)
|
||||
try:
|
||||
tdesk = await client.ToTDesktop(flag=UseCurrentSession)
|
||||
if not tdesk.isLoaded():
|
||||
raise RuntimeError("TDesktop failed to load after conversion")
|
||||
if os.path.exists(out_dir):
|
||||
shutil.rmtree(out_dir)
|
||||
tdesk.SaveTData(out_dir)
|
||||
finally:
|
||||
await client.disconnect()
|
||||
|
||||
asyncio.run(_run())
|
||||
|
||||
|
||||
def zip_tdata(tdata_dir: str, zip_path: str):
|
||||
"""Zip the tdata folder into a zip file."""
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
for root, dirs, files in os.walk(tdata_dir):
|
||||
for f in files:
|
||||
full = os.path.join(root, f)
|
||||
arc = os.path.relpath(full, os.path.dirname(tdata_dir))
|
||||
zf.write(full, arc)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("usage: tglib_to_tdata.py <input.json> <output.zip>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
input_json, output_zip = sys.argv[1], sys.argv[2]
|
||||
payload = json.load(open(input_json))
|
||||
auth_key, dc_id, user_id = extract_keys(payload)
|
||||
print(f"auth_key: {len(auth_key)}B, dc_id: {dc_id}, user_id: {user_id}", file=sys.stderr)
|
||||
|
||||
tmpdir = tempfile.mkdtemp(prefix="tglib_tdata_")
|
||||
try:
|
||||
session_file = make_session(tmpdir, auth_key, dc_id)
|
||||
tdata_dir = os.path.join(tmpdir, "tdata")
|
||||
convert_to_tdata(session_file, tdata_dir)
|
||||
zip_tdata(tdata_dir, output_zip)
|
||||
print(f"wrote {output_zip} ({os.path.getsize(output_zip)} bytes)", file=sys.stderr)
|
||||
finally:
|
||||
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user