From 2a52cb81744a55251dfd05cc1b2c4ee8dd311ebe Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 31 Jul 2026 09:00:21 +0200 Subject: feat: refuse to back up a different chat into an existing archive dir last_id is a high-water mark for one specific chat. Applied as min_id to a different one it silently skips that chat's history and the run still reports success. Compare the requested target against the one saved in state.json and exit before any network work when they differ. The comparison is textual, ignoring case and a leading @; resolving a username to its numeric ID would need a logged-in client, so switching between the two forms of the same chat also refuses. --force-target overrides and keeps the existing resume position. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'CLAUDE.md') diff --git a/CLAUDE.md b/CLAUDE.md index c0af417..e0fb0e1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,7 @@ Resume/dedup is also file-based: media is named `{message.id}_{sanitized_name}` `--archive-dir` holds media files, `state.json`, and `failures.json`, and is created if missing (`parents=True`). It is per-chat: one dir per backed-up chat. -`state.json` also stores the `target` the dir was last backed up with, so subsequent runs need only `--archive-dir`. An explicit `--target` still wins; it is only required for a dir with no saved target (a first run, or one created before this was added). Nothing guards against pointing an existing dir at a *different* chat: `last_id` from the old chat would be applied as `min_id` to the new one and skip its history. +`state.json` also stores the `target` the dir was last backed up with, so subsequent runs need only `--archive-dir`. An explicit `--target` still wins; it is only required for a dir with no saved target (a first run, or one created before this was added). Pointing an existing dir at a *different* chat is refused before any network work (`same_target` in `run_backup`), because `last_id` from the old chat would be applied as `min_id` to the new one and skip its history while reporting success. The comparison is textual, ignoring case and a leading `@`; it cannot tell that `@foo` and `-100…` are the same chat, since resolving that needs a logged-in client, so that case refuses too. `--force-target` overrides and keeps the existing resume position. `failures.json` is append-only JSONL, one `{"id", "file", "error"}` object per line, written only when a download exhausts `MAX_RETRIES`. JSONL rather than a JSON array so appending is a single write with no read-modify-write: a crash costs the last line, not the file. Read it with `jq -s`. Nothing reads or prunes it, and a message failing on two runs is logged twice. -- cgit v1.2.3