Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,44 @@ docker compose exec telegram-backup python -m src backup

# Re-authenticate (if session expires)
docker compose exec -it telegram-backup python -m src auth

# Import a Telegram Desktop export
docker compose exec telegram-backup python -m src import -p /data/exports/ChatExport
```

### Importing Telegram Desktop Exports

You can import chat history exported from Telegram Desktop (Settings > Advanced > Export Telegram data) into Telegram-Archive. The exported chat will appear in the web viewer just like live-backed-up chats.

```bash
# Basic import (auto-detects chat ID from export)
telegram-archive import -p /path/to/ChatExport_2024-01-15

# Import with explicit chat ID (marked format)
telegram-archive import -p /path/to/export -c -1001234567890

# Dry run — validate without writing anything
telegram-archive import -p /path/to/export --dry-run

# Import text only, skip media files
telegram-archive import -p /path/to/export --skip-media

# Merge into an existing chat (add/update messages)
telegram-archive import -p /path/to/export --merge
```

**Flags:**

| Flag | Description |
|------|-------------|
| `-p, --path` | Path to export folder containing `result.json` (required) |
| `-c, --chat-id` | Override chat ID in marked format (e.g., `-1001234567890`) |
| `--dry-run` | Parse and validate without writing to DB or copying media |
| `--skip-media` | Import only messages and metadata, skip media files |
| `--merge` | Allow importing into a chat that already has messages |

**Supported content:** Text messages, photos, videos, documents, voice messages, stickers, animations, service messages (pins, group actions), forwarded messages, replies, and edited messages.

## Data Storage

```
Expand Down
14 changes: 14 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ For upgrade instructions, see [Upgrading](#upgrading) at the bottom.

## [Unreleased]

## [6.4.0] - 2026-02-25

### Added

- **Import Telegram Desktop chat exports** — New `telegram-archive import` CLI command reads Telegram Desktop exports (`result.json` + media folders) and inserts them into the database. Imported chats appear in the web viewer like any other backed-up chat. Supports both single-chat and full-account exports. Closes [#81](https://github.com/GeiserX/Telegram-Archive/issues/81).
- `--path` — Path to export folder containing `result.json`
- `--chat-id` — Override chat ID (marked format)
- `--dry-run` — Validate without writing to DB or copying media
- `--skip-media` — Import only messages/metadata
- `--merge` — Allow importing into a chat that already has messages
- Handles text messages, photos, videos, documents, voice messages, stickers, and service messages (pins, group actions, etc.)
- Forwards, replies, and edited messages are preserved with full metadata
- Media files are copied into the standard media directory structure

## [6.3.2] - 2026-02-17

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "telegram-archive"
version = "6.3.1"
version = "6.4.0"
description = "Automated Telegram backup with Docker. Performs incremental backups of messages and media on a configurable schedule."
readme = "README.md"
requires-python = ">=3.14"
Expand Down
62 changes: 62 additions & 0 deletions src/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def create_parser() -> argparse.ArgumentParser:
telegram-archive stats # Show backup statistics
telegram-archive export -o file.json # Export to JSON

4. Import Telegram Desktop exports:
telegram-archive import -p /path/to/export
telegram-archive import -p /path/to/export -c -1001234567890 --merge

LOCAL DEVELOPMENT:

Use --data-dir to specify an alternative data location (default: /data):
Expand Down Expand Up @@ -109,6 +113,26 @@ def create_parser() -> argparse.ArgumentParser:
"list-chats", help="List all backed up chats", description="Show a table of all chats in the backup database."
)

# Import command
import_parser = subparsers.add_parser(
"import",
help="Import Telegram Desktop chat export",
description="Import a Telegram Desktop chat export (result.json + media) into the database.",
)
import_parser.add_argument("-p", "--path", required=True, help="Path to export folder containing result.json")
import_parser.add_argument(
"-c", "--chat-id", type=int, help="Override chat ID (marked format, e.g. -1001234567890)"
)
import_parser.add_argument(
"--dry-run", action="store_true", help="Parse and validate without writing to DB or copying media"
)
import_parser.add_argument(
"--skip-media", action="store_true", help="Import only messages/metadata, skip media files"
)
import_parser.add_argument(
"--merge", action="store_true", help="Allow importing into a chat that already has messages"
)

return parser


Expand Down Expand Up @@ -172,6 +196,42 @@ async def run_list_chats(args) -> int:
return 1


async def run_import(args) -> int:
"""Run import command."""
from .config import Config, setup_logging
from .telegram_import import TelegramImporter

try:
config = Config()
setup_logging(config)

importer = await TelegramImporter.create(config.media_path)
try:
summary = await importer.run(
export_path=args.path,
chat_id_override=args.chat_id,
dry_run=args.dry_run,
skip_media=args.skip_media,
merge=args.merge,
)
prefix = "[DRY RUN] " if args.dry_run else ""
print(f"\n{prefix}Import complete:")
print(f" Chats: {summary['chats_imported']}")
print(f" Messages: {summary['total_messages']}")
print(f" Media files: {summary['total_media']}")
for detail in summary["details"]:
print(
f" - {detail['chat_name']} (ID {detail['chat_id']}): "
f"{detail['messages']} messages, {detail['media']} media"
)
finally:
await importer.close()
return 0
except Exception as e:
print(f"Import failed: {e}", file=sys.stderr)
return 1


def run_auth(args) -> int:
"""Run authentication setup."""
from .setup_auth import main as auth_main
Expand Down Expand Up @@ -231,6 +291,8 @@ def main() -> int:
return asyncio.run(run_stats(args))
elif args.command == "list-chats":
return asyncio.run(run_list_chats(args))
elif args.command == "import":
return asyncio.run(run_import(args))
else:
parser.print_help()
return 0
Expand Down
Loading
Loading