#!/usr/bin/env python3 """Выгрузка публичной группы/канала, в которой мы не состоим. `export.py run` ходит по iter_dialogs и потому видит только свои чаты. Здесь сущность резолвится по @username и история читается напрямую. uv run fetch_public.py @cyschools --since 2026-03-04 """ from __future__ import annotations import argparse import asyncio import datetime as dt import json from pathlib import Path from telethon import TelegramClient import export as ex ex.load_dotenv(ex.ROOT / ".env") async def main_async(args) -> None: api_id, api_hash = ex.require_credentials() client = TelegramClient(args.session, api_id, api_hash) client.flood_sleep_threshold = 24 * 60 * 60 await client.start() try: entity = await client.get_entity(args.chat) dialog_id = int(f"-100{entity.id}") folder = Path(args.out) / "chats" / f"{dialog_id}_{ex.slugify(getattr(entity, 'title', args.chat))}" folder.mkdir(parents=True, exist_ok=True) since = dt.datetime.fromisoformat(args.since).replace(tzinfo=dt.timezone.utc) (folder / "meta.json").write_text(json.dumps({ "id": dialog_id, "name": getattr(entity, "title", None), "kind": ex.dialog_kind(entity), "username": getattr(entity, "username", None), "fetched_since": args.since, }, ensure_ascii=False, indent=2), encoding="utf-8") cache: dict = {} n = 0 with (folder / "messages.jsonl").open("w", encoding="utf-8") as jsonl, \ (folder / "messages_pretty.jsonl").open("w", encoding="utf-8") as pretty: async for msg in client.iter_messages(entity, offset_date=since, reverse=True): record = msg.to_dict() sender = await ex.sender_label(client, msg, cache) jsonl.write(json.dumps(record, ensure_ascii=False, cls=ex.Encoder) + "\n") pretty.write(json.dumps(ex.simplify_message(msg, sender, record), ensure_ascii=False, cls=ex.Encoder) + "\n") n += 1 if n % 1000 == 0: print(f" … {n} ({msg.date:%Y-%m-%d})", flush=True) print(f"{folder.name}: {n} сообщений") finally: await client.disconnect() def main() -> None: p = argparse.ArgumentParser() p.add_argument("chat") p.add_argument("--since", required=True) p.add_argument("--out", default=str(ex.ROOT / "out")) p.add_argument("--session", default=str(ex.ROOT / "tg_export.session")) asyncio.run(main_async(p.parse_args())) if __name__ == "__main__": main()