diff options
| author | Danilo M. <danix@danix.xyz> | 2026-07-30 20:38:19 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-07-30 20:43:13 +0200 |
| commit | 1b7325fec54a419b7773b31e1032717279713b29 (patch) | |
| tree | fc76df00c2838c2be851d0197e4c6743e71520c1 /README.md | |
| download | tg_backup-1b7325fec54a419b7773b31e1032717279713b29.tar.gz tg_backup-1b7325fec54a419b7773b31e1032717279713b29.zip | |
feat: incremental Telegram media backup
Single-file tool that downloads photos and documents from a Telegram
chat and resumes where it left off.
Durability:
- process oldest-to-newest and persist last_id after every message, so
an interrupted run resumes instead of rescanning
- write state.json via temp file + rename; a torn write would otherwise
leave unparseable JSON and block the next run
- download to a .part file and rename on success, so a killed run cannot
leave a truncated file that the exists() check treats as complete
- retry transient errors with exponential backoff alongside the existing
FloodWait handling, and skip permanently failed files rather than
stalling the run
Usability:
- share one session across all archive dirs, so a login covers every
chat instead of one per directory; migrate an existing per-archive
session in place rather than forcing re-authentication
- create the config template before argument parsing, since --target was
required but unknowable before the API keys were set
- add --list-chats to print dialog IDs and usernames
- convert numeric targets to int; Telethon resolves a numeric string as
a username and never consults the entity cache
Hardening:
- strip path components from sender-controlled filenames
- create config and session files 0600 in a 0700 directory
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..32a7fe2 --- /dev/null +++ b/README.md @@ -0,0 +1,116 @@ +# tg_backup + +Incremental media backup for Telegram chats. Downloads photos and documents from a chat, remembers where it stopped, and picks up from there on the next run. + +Single Python file, one dependency ([Telethon](https://docs.telethon.dev/)). + +## Install + +```bash +pip install telethon +cp tg_backup.py ~/bin/ && chmod +x ~/bin/tg_backup.py +``` + +## Setup + +Run it once with no arguments to create the config: + +```bash +tg_backup.py +``` + +It writes `~/.config/telegram_backup/config.json` and exits. Fill in the two values from [my.telegram.org](https://my.telegram.org) (API development tools): + +```json +{ + "api_id": 12345, + "api_hash": "<your 32-character hash>" +} +``` + +`api_id` is an integer, `api_hash` a 32-character hex string. + +Nothing else from that page is needed. FCM credentials and MTProto server addresses are for full client implementations. + +The first real run prompts for your phone number and login code. That session is saved and shared by every subsequent run, so you log in once. + +## Usage + +List your chats to find a target: + +```bash +tg_backup.py --list-chats +``` + +``` + ID TYPE USERNAME NAME + -1001234567890 group @examplegroup Example Group + 123456789 user @examplecontact Example Contact + 987654321 user - Contact Without Handle +``` + +Back one up: + +```bash +tg_backup.py --target=@examplegroup --archive-dir ~/backups/examplegroup +tg_backup.py --target=-1001234567890 --archive-dir ~/backups/examplegroup +``` + +Numeric IDs are more durable than usernames, which their owners can change. Chats showing `-` have no public handle and can only be addressed by ID. + +`--target` also accepts `me` (Saved Messages), t.me links, and invite links for groups you have already joined. + +### Options + +| Option | Default | Meaning | +|---|---|---| +| `--target` | required | Chat to back up: `@username`, numeric ID, t.me link, or `me` | +| `--archive-dir` | `./tg_archive` | Where media and resume state are written; created if missing | +| `--list-chats` | | Print your dialogs with IDs and usernames, then exit | +| `--self-check` | | Run internal assertions and exit. No network, no login | + +Use one archive directory per chat. Prefer absolute paths: the default is relative to the current directory, so running from elsewhere silently starts a second archive. + +### Negative IDs need `=` + +```bash +tg_backup.py --target=-1001234567890 # correct +tg_backup.py --target -1001234567890 # error: expected one argument +``` + +Without `=`, argparse reads the leading `-` as the start of another flag. + +## How it works + +Messages are processed oldest to newest, and the last message ID seen is written to `state.json` in the archive directory after every message. An interrupted run resumes from there instead of rescanning the whole chat. + +Media is saved as `{message_id}_{original_name}`. Downloads land in a temporary `.part` file and are renamed only on success, so a killed run never leaves a truncated file that a later run would mistake for a complete one. Files that already exist are skipped, so even a lost `state.json` degrades to a slow rescan rather than re-downloading everything. + +Rate limits are handled at two levels: Telegram's requested wait is honoured for both individual downloads and history fetches, and other errors (dropped connections, expired file references) are retried with exponential backoff. A download that fails permanently is logged and skipped rather than stalling the run forever. + +Only media is saved. Message text and captions are not. + +## Files + +| Path | Contents | +|---|---| +| `~/.config/telegram_backup/config.json` | API credentials | +| `~/.config/telegram_backup/session.session` | Login session, shared across all archive directories | +| `<archive-dir>/state.json` | Resume position for that chat | +| `<archive-dir>/*` | Downloaded media | + +The session file is account credentials: anyone holding it can read your Telegram. It is created mode `0600` inside a `0700` directory. Keep it out of version control and off shared storage. + +## License + +Copyright (C) 2026 Danilo M. <danix@danix.xyz> + +Released under the GNU General Public License version 2. See [LICENSE](LICENSE) for the full text. + +## Development Approach + +This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback. + +All contributions are reviewed, tested, and curated by the maintainer before being included in the codebase. AI is used as a productivity and exploration tool, while human oversight remains central to all decisions. + +The goal is to combine the flexibility of AI-assisted development with standard open-source practices such as transparency, review, and accountability. |
