Metadata-Version: 2.4
Name: tg-export
Version: 2.0.0
Summary: Flexible Telegram data export tool
Author-email: Vitaly Ostanin <vitaly.ostanin@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/VitalyOstanin/tg-export
Project-URL: Repository, https://github.com/VitalyOstanin/tg-export
Project-URL: Issues, https://github.com/VitalyOstanin/tg-export/issues
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: System :: Archiving :: Backup
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: telethon<2,>=1.43
Requires-Dist: pyyaml>=6.0
Requires-Dist: aiosqlite>=0.20
Requires-Dist: jinja2>=3.1.6
Requires-Dist: click>=8.3.3
Requires-Dist: rich>=15.0
Requires-Dist: python-socks[asyncio]<3,>=2.0
Provides-Extra: proxy
Dynamic: license-file

# tg-export

[![CI](https://github.com/VitalyOstanin/tg-export/actions/workflows/ci.yml/badge.svg)](https://github.com/VitalyOstanin/tg-export/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/VitalyOstanin/tg-export/graph/badge.svg?branch=master)](https://codecov.io/gh/VitalyOstanin/tg-export)
[![PyPI version](https://img.shields.io/pypi/v/tg-export.svg)](https://pypi.org/project/tg-export/)

Экспорт данных из Telegram на локальный диск с гибкой настройкой.

## Содержание

- [Возможности](#возможности)
- [Сценарии использования](#сценарии-использования)
- [Установка](#установка)
- [Быстрый старт](#быстрый-старт)
- [Структура конфигов](#структура-конфигов)
- [Глобальные опции](#глобальные-опции)
  - [Коды возврата](#коды-возврата)
- [Документация](#документация)
- [Автодополнение командной строки](#автодополнение-командной-строки)
- [Разработка](#разработка)
- [Лицензия](#лицензия)

## Возможности

- **Инкрементальный экспорт** -- при повторном запуске скачиваются только новые сообщения и файлы, состояние хранится в SQLite
- **Sibling-дедупликация** -- при экспорте нескольких аккаунтов (семья, рабочий/личный) общие файлы не скачиваются повторно, а линкуются через hardlink
- **Импорт из tdesktop** -- файлы из стандартного экспорта Telegram Desktop копируются вместо повторного скачивания
- **Takeout API** -- использует официальный Takeout для обхода rate-limiting
- **Гибкие правила экспорта** -- настройка по чатам, папкам, типам (personal, groups, channels, bots), с фильтрами по датам, типам медиа и размеру файлов
- **Логика архивных чатов** -- чат считается архивным (`is_archived`) только если он присутствует исключительно в архиве Telegram. Если чат есть в основном списке диалогов или в именованной папке, он не помечается как архивный, даже если одновременно находится в архиве
- **Проверка размера в runtime** -- если реальный размер файла превышает лимит, скачивание прерывается и частичный файл удаляется
- **HTML-рендеринг по месяцам** -- каждый месяц в отдельном файле, с оглавлением (TOC) и навигацией prev/next
- **Progress bars** -- основной прогресс по сообщениям + sub-progress bars для каждого скачиваемого файла
- **Контроль свободного места** -- экспорт завершается ошибкой, если свободное место на диске падает ниже порога (`min_free_space` в `config.yaml`, по умолчанию 20 GB); состояние при этом сохранено, и следующий запуск продолжает с той же точки
- **Корректный shutdown** -- Ctrl+C сохраняет состояние; второй Ctrl+C в течение трёх секунд прерывает немедленно
- **Поддержка прокси** -- SOCKS5, SOCKS4, HTTP прокси для подключения к Telegram API
- **Данные в SQLite** -- все сообщения и метаданные хранятся в SQLite, что позволяет пересоздать HTML без обращения к Telegram API (`tg-export run --rerender`), строить поисковые индексы, подключить веб-интерфейс или использовать данные в любых других целях
- **CLI для анализа** -- команда `tg info` для batch-запросов информации о чатах через API
- **Очистка данных чата** -- команда `purge` для удаления данных конкретного чата из БД и с диска

## Сценарии использования

**Личный архив** -- экспорт всех личных переписок и групп с медиафайлами на локальный диск для долгосрочного хранения.

**Семейный экспорт** -- экспорт аккаунтов нескольких членов семьи. Общие группы и каналы содержат одинаковые файлы -- sibling-дедупликация экономит место на диске через hardlink.

**Выборочный экспорт** -- экспорт только нужных чатов/папок с правилами: рабочие чаты экспортируются, боты и публичные каналы пропускаются, для флудилок скачиваются только фото.

**Миграция с tdesktop** -- если уже есть экспорт из Telegram Desktop, файлы из него импортируются без повторного скачивания.

## Установка

Требуется Python 3.11 или новее. Проверяется на Linux с версиями 3.11-3.14; на macOS
работа ожидается, но не проверяется. На Windows проект не проверяется: блокировка файла
сессии там вырождается в отсутствующую (нет `fcntl`), то есть от запуска второго процесса
на том же аккаунте ничто не защищает.

Из PyPI:

```bash
uv tool install tg-export
```

Тем же набором ставится и `pip install tg-export`. Поддержка прокси (`python-socks`)
входит в зависимости пакета: отдельного набора устанавливать не нужно, а прежнее
написание `tg-export[proxy]` продолжает работать и ничего не добавляет.

Из исходников:

```bash
git clone https://github.com/VitalyOstanin/tg-export.git
cd tg-export
uv sync
```

`uv sync` создаёт окружение `.venv` и ставит в него проект с зависимостями; команды
после этого запускаются как `uv run tg-export ...` из каталога проекта. Отдельная
`uv pip install -e .` окружения не создаёт и на свежем клоне завершается ошибкой
`No virtual environment found` -- ей нужен предварительный `uv venv`.

Инструменты разработки (pytest, ruff, pyright) объявлены группой `dev` и ставятся
`uv sync` по умолчанию -- отдельного флага для них не нужно, см. раздел
[Разработка](#разработка).

## Быстрый старт

Примеры ниже записаны для пакета, установленного из PyPI: исполняемый файл `tg-export`
доступен в PATH. При работе из исходников те же команды запускаются с префиксом:
`uv run tg-export ...` из каталога проекта.

### 1. Получить API credentials

Зайти на [my.telegram.org](https://my.telegram.org), создать приложение, получить `api_id` и `api_hash`.

```bash
tg-export auth credentials
```

Ввести `api_id` и `api_hash`. Они сохраняются в `~/.config/tg-export/api_credentials.yaml` и используются для всех аккаунтов.

### 2. Настроить прокси (если нужен)

Создать файл `~/.config/tg-export/config.yaml`:

```yaml
proxy:
  type: socks5      # socks5, socks4, http
  host: 127.0.0.1
  port: 1080
  # username: user   # опционально
  # password: pass   # опционально
```

### 3. Залогиниться в Telegram

```bash
tg-export auth add --name myaccount
```

Ввести номер телефона и код подтверждения. Сессия сохранится в `~/.config/tg-export/sessions/myaccount.session`.

Для нескольких аккаунтов повторить с разными именами. Установить аккаунт по умолчанию:

```bash
tg-export account default myaccount
```

### 4. Анализ чатов и каталог

Получить каталог всех чатов аккаунта:

```bash
tg-export list --output-file catalog.yaml
tg-export list --json --output-file catalog.json
```

Посмотреть информацию о конкретных чатах (количество сообщений, последние сообщения):

```bash
tg-export tg info 123456789 987654321
tg-export tg info --from-catalog catalog.json --type personal -n 3
```

Посмотреть последние сообщения чата. Текст по умолчанию обрезается до 200 символов; `--truncate N` задаёт другую длину, `--truncate 0` и `--no-truncate` печатают текст целиком:

```bash
tg-export tg messages 123456789 -n 5
tg-export tg messages 123456789 -n 5 --truncate 1000
tg-export tg messages 123456789 -n 5 --no-truncate
```

### 5. Настройка правил экспорта

Сгенерировать шаблон конфига:

```bash
tg-export init
```

Конфиг создается в `~/.config/tg-export/myaccount.yaml`. Настроить правила: какие чаты экспортировать, какие пропускать, какие типы медиа скачивать.

AI-агенты (Claude Code, Cursor и т.д.) могут помочь с настройкой: показать список чатов по категориям, задать вопросы о каждом и сформировать конфиг интерактивно.

Пример конфига:

```yaml
output:
  path: ./export_output    # экспорт аккаунта ляжет в ./export_output/<alias>/

defaults:
  media:
    types: [all]
    max_file_size: 100MB
    concurrent_downloads: 3

# Импорт файлов из существующего экспорта tdesktop
import_existing:
  - path: ~/Downloads/Telegram Desktop/DataExport
    type: tdesktop

# Правила по типам чатов
type_rules:
  bots:
    skip: true
  public:
    skip: true
  personal: {}    # экспортировать все личные чаты
  self: {}        # экспортировать Saved Messages

# Правила по папкам Telegram
folders:
  Work: {}
  Family:
    media:
      types: [photo, video]

# Правила по конкретным чатам
chats:
  - id: 777000
    name: Telegram
    skip: true
  - id: 123456789
    name: "Important Chat"
    media:
      types: []    # без медиа

# Что делать с чатами, не попавшими ни в одно правило
unmatched:
  action: skip    # skip | export_with_defaults
```

### 6. Запустить экспорт

```bash
tg-export run
```

Для пробного запуска без скачивания:

```bash
tg-export run --dry-run
```

При повторном запуске скачиваются только новые сообщения (инкрементальный экспорт).

Экспорт идёт через Takeout API. Когда Takeout недоступен -- действует кулдаун
`TAKEOUT_INIT_DELAY`, запрос не подтверждён в клиенте Telegram или сервер
отказал, -- tg-export сообщает причину и продолжает через обычный API: результат
тот же, но медленнее и с обычными ограничениями частоты запросов. Использованный
режим печатается в итоговой сводке строкой `API:`. Чтобы отказ от Takeout был
ошибкой, а не переходом на обычный API:

```bash
tg-export run --require-takeout
```

Обратный случай -- когда Takeout не нужен и ждать кулдаун незачем: запрос не
делается вовсе, экспорт сразу идёт через обычный API.

```bash
tg-export run --no-takeout
```

Takeout-сессия живёт между запусками: по окончании экспорта она не завершается,
а только отпускается, и следующий запуск переиспользует её идентификатор. Это
существенно, потому что на создание новой сессии Telegram отвечает кулдауном
`TAKEOUT_INIT_DELAY` длиной до 24 часов -- завершая сессию каждый раз, Takeout
удавалось бы использовать не чаще раза в сутки. Если сервер уже забыл сохранённый
идентификатор, tg-export это обнаруживает пробным запросом и создаёт новую сессию.
Завершить сессию явно:

```bash
tg-export takeout clear
```

### 7. Sibling-дедупликация

Каждый аккаунт экспортируется в свой подкаталог `./export_output/<alias>/`. Соседние базы данных tg-export находит автоматически и использует уже скачанные файлы через hardlink:

```
export_output/
  account1/         # первый аккаунт
  account2/         # файлы из общих чатов линкуются из account1
```

## Структура конфигов

```
~/.config/tg-export/
  api_credentials.yaml      # API ID и Hash (общие для всех аккаунтов)
  config.yaml               # Глобальные настройки (proxy, min_free_space)
  default_account            # Имя аккаунта по умолчанию
  sessions/
    myaccount.session        # Telethon-сессия
  myaccount.yaml             # Конфиг экспорта для аккаунта

export_output/
  myaccount/
    .tg-export-state.db      # SQLite: состояние экспорта, сообщения, файлы
    index.html               # Оглавление выгрузки
    personal_info.html       # Профиль (флаг personal_info)
    contacts.html            # Контакты (флаг contacts)
    sessions.html            # Активные сессии (флаг sessions)
    userpics.html            # Фото профиля (флаг userpics)
    stories.html             # Истории (флаг stories)
    other_data.html          # Сохранённые рингтоны (флаг profile_music)
    profile_photos/          # Файлы фото профиля
    stories/                 # Файлы историй
    ringtones/               # Файлы рингтонов
    css/, js/, images/       # Оформление страниц
    unfiled/                 # Чаты вне папок
      Chat_Name_123456/
        messages.html         # Redirect на первый месяц
        messages_2024-01.html # Сообщения за январь 2024
        messages_2024-02.html # Сообщения за февраль 2024
        photos/               # Скачанные фото
        videos/               # Скачанные видео
        files/                # Скачанные документы
      Channel_Name_222/       # Канал с монофорумом
        Monoforum_Name_333/   # Монофорум вложен в каталог своего канала
    folders/                  # Чаты, разложенные по папкам Telegram
      Work/
        Chat_Name_444/
    archived/                 # Чаты только из архива Telegram
      Chat_Name_555/
    left/                     # Покинутые каналы и группы
      Channel_Name_666/
```

Подкаталог с медиа создаётся только при наличии соответствующих файлов; полный
перечень подкаталогов по типам медиа -- в [docs/configuration.md](docs/configuration.md#типы-медиа).

## Глобальные опции

Опции указываются до подкоманды (`tg-export <опция> <команда>`):

| Опция               | Назначение                                                                                  |
| ------------------- | ------------------------------------------------------------------------------------------- |
| `--debug`           | Включить отладочное логирование (принудительно уровень `DEBUG`).                             |
| `--log-level LEVEL` | Уровень логирования: `DEBUG`/`INFO`/`WARNING`/`ERROR`/`CRITICAL`. Переопределяет `TG_EXPORT_LOG_LEVEL` и `LOG_LEVEL`. Суффикс `:all` (`DEBUG:all`) добавляет вывод telethon и aiosqlite. |
| `--quiet`, `-q`     | Подавить прогресс и статусные строки; ошибки и итоговая сводка остаются.                     |

Приоритет уровня логирования (по убыванию): `--debug` > `--log-level` > переменная окружения `TG_EXPORT_LOG_LEVEL` > `LOG_LEVEL` > значение по умолчанию `WARNING`. Имя без префикса принимается ради совместимости и стоит ниже: оно не принадлежит ни одному инструменту.

Логи самих библиотек (telethon пишет каждый пакет MTProto, aiosqlite -- каждый запрос) держатся на уровне `WARNING` независимо от собственного: иначе `--debug` тонет в их выводе. Включаются суффиксом `:all` -- `--log-level DEBUG:all` или `TG_EXPORT_LOG_LEVEL=DEBUG:all`.

Потоки вывода: машиночитаемый вывод команд `list`, `state show`, `tg info`, `tg messages` идёт в stdout; прогресс, статусы, диагностика и ошибки — в stderr. Это позволяет безопасно использовать пайпинг, например `tg-export list --json | jq ...`.

Флаг `--json` для машиночитаемого вывода поддерживают команды `list`, `config`, `account list`, `account default`, `auth check`, `state show`, `tg info`, `tg messages` и `tg download`. В этом режиме в stdout печатается только JSON.

### Коды возврата

| № | Код       | Когда возвращается                                                                                       |
|---|-----------|----------------------------------------------------------------------------------------------------------|
| 1 | `0`       | Команда выполнена успешно.                                                                                |
| 2 | `1`       | Команда сообщила об отказе: аккаунт не найден, сессия непригодна, сообщение не найдено, часть получателей не получила сообщение, экспорт завершился с ошибками, вопрос некому ответить (пустой stdin вместо терминала). |
| 3 | `2`       | Ошибка разбора аргументов (проверка Click).                                                               |
| 4 | `130`     | Прерывание по SIGINT (Ctrl+C), в том числе до старта экспорта.                                             |
| 5 | `143`     | Завершение по SIGTERM во время экспорта.                                                                  |
| 6 | `141`     | Закрытый конвейер: читатель на другом конце вышел раньше (`tg-export list | head`), и команда завершается сигналом SIGPIPE, как принято в оболочках. |

Коды `130`, `141` и `143` следуют соглашению `128 + номер сигнала`. Запуск без терминала на входе (cron, systemd, CI, `< /dev/null`) на команде, задающей вопрос, получает код `1` и сообщение с именем флага, которым вопрос обходится (`--yes` у `purge` и `state reset`, `--name`/`--api-id`/`--api-hash` у команд `auth`): сигнала не было, и код `130` о нём лгал. Экспорт, завершившийся с ошибками при загрузке, возвращает `1`, даже если часть чатов выгружена: число ошибок печатается в итоговой сводке строкой `Errors:`.

## Документация

- [docs/cli.md](docs/cli.md) -- справочник команд: назначение, опции и коды возврата каждой команды.
- [docs/configuration.md](docs/configuration.md) -- подробное описание YAML-конфигурации экспорта (правила, фильтры, типы медиа, настройки выгрузки).
- [config.example.yaml](config.example.yaml) -- рабочий пример конфигурации со всеми разделами; копируется в `~/.config/tg-export/<алиас>.yaml` и правится под себя.
- [CHANGELOG.md](CHANGELOG.md) -- список изменений между версиями.
- [CONTRIBUTING.md](CONTRIBUTING.md) -- руководство для контрибьюторов: карта модулей пакета («Устройство пакета»), установка dev-окружения, запуск тестов, формат коммитов.
- [docs/adr/](docs/adr/) -- архитектурные решения (ADR) в формате MADR.
- [RELEASING.md](RELEASING.md) -- процесс выпуска версий и конвенции тегов.
- [SECURITY.md](SECURITY.md) -- как сообщить об уязвимости приватно, минуя публичные issues.

## Автодополнение командной строки

tg-export использует Click, который поддерживает автодополнение для bash/zsh/fish. Включить его можно через переменную окружения (добавьте строку в `~/.bashrc`, `~/.zshrc` или конфиг fish):

```bash
# bash
eval "$(_TG_EXPORT_COMPLETE=bash_source tg-export)"
# zsh
eval "$(_TG_EXPORT_COMPLETE=zsh_source tg-export)"
# fish
_TG_EXPORT_COMPLETE=fish_source tg-export | source
```

## Разработка

Установка окружения вместе с инструментами разработки (группа `dev` входит в набор
по умолчанию):

```bash
uv sync
```

Весь набор проверок, который прогоняет CI -- синхронизация `uv.lock`, линтер, формат,
типы, тесты с покрытием и границы покрытия по модулям -- запускается одной командой;
она же приводит в порядок исправимое:

```bash
./scripts/check.sh
./scripts/check.sh --fix
```

Отдельный прогон тестов (порог покрытия применяется к полному прогону, поэтому для
одного файла его снимают):

```bash
uv run python -m pytest
uv run python -m pytest tests/test_models.py --no-cov
```

Подробнее -- в [CONTRIBUTING.md](CONTRIBUTING.md).

## Лицензия

[MIT License](LICENSE)
