aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-07-30 20:38:19 +0200
committerDanilo M. <danix@danix.xyz>2026-07-30 20:43:13 +0200
commit1b7325fec54a419b7773b31e1032717279713b29 (patch)
treefc76df00c2838c2be851d0197e4c6743e71520c1 /README.md
downloadtg_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.md116
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.