Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Personal Telegram Archiver

Персональный архиватор Telegram, работающий от вашего собственного аккаунта через официальный клиентский API (MTProto). Он сохраняет сообщения и медиа из доступных вам чатов в локальный архив (SQLite + файлы) и, опционально, отправляет копию в ваш приватный архивный чат.

Это не бот и не сервис для других людей. Один пользователь — вы.


Содержание

  1. Что делает программа
  2. Ограничения Telegram API
  3. Чего программа НЕ может
  4. Как получить Telegram API credentials
  5. Установка Python
  6. Установка зависимостей
  7. Создание .env
  8. Запуск — включая запуск с телефона
  9. Авторизация
  10. Настройка архивного чата
  11. Первоначальный импорт
  12. Постоянный мониторинг
  13. Развёртывание на Railway
  14. Где находятся файлы
  15. Восстановление после сбоя
  16. Обновление зависимостей
  17. Архитектура
  18. Безопасность
  19. Что можно добавить дальше

1. Что делает программа

Программа подключается к вашему аккаунту как обычный Telegram-клиент и:

  • следит за новыми сообщениями во всех диалогах, доступных аккаунту: личные переписки, группы, супергруппы, каналы, где вы состоите;
  • немедленно сохраняет сообщение в локальную базу — это происходит за миллисекунды и именно поэтому сообщение, удалённое через пару секунд, всё равно остаётся в архиве;
  • скачивает медиафайлы на диск: фото, видео, документы, аудио, голосовые, видеосообщения, GIF/анимации, стикеры;
  • отправляет копию в ваш приватный архивный чат (если задан ARCHIVE_CHAT_ID);
  • отслеживает редактирование: сохраняются все версии — исходная и каждая изменённая, с временем изменения;
  • отслеживает удаление: архивная копия остаётся нетронутой и помечается флагом «удалено в оригинале»;
  • умеет импортировать существующую историю (--import) с возобновлением с места остановки.

Что сохраняется для каждого сообщения

Поле Сохраняется
Текст да
Подписи к медиа да
ID сообщения, ID чата да
Дата и время (UTC) да
Информация об отправителе (id, имя, username, бот/не бот) да
Направление (входящее/исходящее) да
Тип сообщения да
Информация о пересылке (кто, когда, автор поста) да
Reply — на какое сообщение отвечает да
Альбомы (несколько медиа в одном сообщении) да, через grouped_id
Ссылки (из текста и из превью) да
Контакты (имя, телефон, vCard) да
Геолокации, live-локации, venue да
Опросы (вопрос и варианты) да
Реакции да, агрегированно (см. ограничения)
Сервисные сообщения (вход в чат, смена аватара и т.п.) да, тип действия
Полный «сырой» ответ API да, JSON в поле raw_json

Что сохраняется для каждого медиафайла

Оригинальный файл на диске, тип, имя файла, MIME type, размер, Telegram file id, DC id, длительность, разрешение, SHA-256 содержимого, дата скачивания, чат, отправитель и подпись (через связь с сообщением).


2. Ограничения Telegram API

Это важная часть. Здесь честно перечислено то, что невозможно — не потому что не реализовано, а потому что API этого не даёт.

Удалённые сообщения

Telegram присылает при удалении только идентификаторы сообщений — без содержимого. Логика такая:

  • Сообщение получено программой до удаления → копия уже в архиве, она сохраняется, ставится пометка об удалении. ✅
  • Сообщение удалено, пока программа не работала и не успела его получить → восстановить содержимое невозможно. Программа зафиксирует факт удаления (таблица deletion_events), но текста и медиа не будет. ❌

Никакого легального способа обойти это нет: у сервера нет обязанности отдавать удалённые данные третьим клиентам, и программа не пытается этого делать.

Практический вывод: чем меньше простоев, тем полнее архив. Для этого и нужен режим постоянного мониторинга.

Редактирование сообщений

Telegram присылает при редактировании только новую версию. История правок на сервере третьим клиентам не отдаётся.

  • Программа видела оригинал → сохранены обе версии (v1, v2, …) с временем. ✅
  • Программа не видела оригинал → сохранится только текущая версия, помеченная как live-edit. Что было до этого — неизвестно. ❌

Self-destructing / view-once медиа (одноразовые фото и видео)

Здесь нужна максимальная честность, потому что вы просили именно её.

Технические факты. Одноразовые фото и видео не защищены криптографически. Механизм «просмотр один раз» реализован на стороне клиента: сервер отдаёт файл обычным способом, а официальное приложение обязуется удалить его после просмотра и уведомить отправителя. Поэтому библиотека вроде Telethon технически способна скачать такой файл.

Что делает этот проект. Программа обнаруживает такое медиа и записывает факт его существования — чат, отправителя, дату, ID сообщения, тип, значение TTL — но не сохраняет содержимое. В базе появляется запись со статусом skipped_self_destructing, в лог пишется предупреждение, а в архивный чат уходит пометка «self-destructing media detected, content NOT archived by design».

Почему так. Это единственное место, где я сознательно не реализовал то, что вы просили, и не хочу это замалчивать. Одноразовое медиа — это приватная гарантия, на которую полагается отправитель, а не вы. Архивирование своей собственной переписки — законное личное дело; тихое сохранение чужого контента, который человек отправил именно с условием «посмотри и оно исчезнет», — уже нарушение его ожиданий, а во многих юрисдикциях и закона о персональных данных. Фальшивую реализацию делать я тоже не стал: обнаружение и логирование работают по-настоящему.

Если для вашего сценария нужно именно сохранение содержимого — это правка в одном месте, MediaDownloader._precheck в app/telegram/media.py, и она целиком на вашу ответственность. Никакого скрытого «переключателя» в конфиге я специально не оставил, чтобы это нельзя было включить случайно.

Секретные чаты

Секретные чаты (end-to-end) недоступны через сторонние клиенты в принципе. Telethon их не поддерживает, ключи существуют только на устройствах-участниках. Такие чаты не будут архивироваться никогда.

Реакции

Программа сохраняет реакции в том виде, в каком API их отдаёт вместе с сообщением: эмодзи и счётчик. Полный список «кто именно поставил реакцию» требует отдельных запросов на каждое сообщение и в больших чатах доступен не полностью — такие запросы здесь не делаются, чтобы не упираться в FloodWait. Изменения реакций отслеживаются через UpdateMessageReactions.

Ограничение сохранения контента (Restricted saving)

Владелец группы или канала может включить запрет на сохранение контента (noforwards). В таких чатах скачивание и пересылка медиа возвращают ошибку. Программа зафиксирует метаданные и запишет ошибку в media_files.download_error, но файл получить не сможет. Это ограничение сервера, обходить его программа не пытается.

Прочие ограничения

  • Только ваши доступы. Программа видит ровно то, что видит ваш аккаунт. Чужие чаты, каналы без подписки, удалённые аккаунты — недоступны.
  • Backlog обновлений ограничен. Если программа была офлайн долго, Telegram отдаст не все пропущенные апдейты. client.catch_up() вызывается при старте, но это best-effort. Пробелы закрывайте через --import.
  • FloodWait. Telegram ограничивает частоту запросов. Программа корректно ждёт указанное время (это нормальная работа, а не ошибка), но большой импорт всё равно займёт часы.
  • Лимит размера файлов. Обычный аккаунт — до 2 ГБ на файл, Premium — до 4 ГБ. Плюс собственный лимит MAX_MEDIA_SIZE_MB (по умолчанию 512 МБ), выше которого файл не скачивается, но метаданные сохраняются.
  • Один сеанс — одно место. Не запускайте одну и ту же сессию одновременно локально и на сервере: апдейты будут делиться между экземплярами и часть сообщений пропадёт из архива.
  • Сопоставление удалений в личных чатах. В личных переписках и обычных группах апдейт об удалении не содержит id чата. Программа опирается на то, что в этом пространстве id сообщений уникальны в рамках аккаунта, — это верно, но для каналов и супергрупп id чата приходит явно и используется напрямую.

Правовая и этическая сторона

Архивировать собственную переписку — ваше право. Но в чатах есть и другие люди. Ответственность за то, как вы храните и используете их сообщения, лежит на вас; в ряде юрисдикций (например, при действии GDPR) на это распространяются требования к обработке персональных данных. Архив — приватный, локальный, никуда не отправляется, кроме вашего собственного архивного чата, но хранить его следует как чувствительные данные.


3. Чего программа НЕ может

Коротким списком, чтобы не было иллюзий:

  • ❌ восстановить сообщение, удалённое до того, как программа его получила;
  • ❌ показать версии сообщения, отредактированного до первого запуска;
  • ❌ сохранить содержимое одноразовых (view-once) фото и видео — сознательное решение, см. раздел выше;
  • ❌ читать секретные (end-to-end) чаты;
  • ❌ читать чаты, к которым нет доступа у вашего аккаунта;
  • ❌ скачивать медиа в чатах с запретом сохранения контента;
  • ❌ узнать, кто именно поставил каждую реакцию, в полном объёме;
  • ❌ обойти FloodWait, лимиты размера файлов или любые ограничения сервера;
  • ❌ работать без периодического онлайна — архив полон настолько, насколько программа была запущена.

4. Как получить Telegram API credentials

  1. Откройте https://my.telegram.org и войдите под своим номером телефона (код придёт в Telegram).
  2. Выберите API development tools.
  3. Заполните форму:
    • App title: например Personal Archiver
    • Short name: например archiver
    • Platform: Desktop
    • URL и описание можно оставить пустыми.
  4. После создания вы увидите App api_id (число) и App api_hash (32 символа).

api_hash — это секрет уровня пароля. Не публикуйте его, не коммитьте в git, не показывайте в скриншотах. Программа никогда не выводит его в лог: за этим следит фильтр в app/logging_setup.py.


5. Установка Python

Нужен Python 3.10 или новее (проверено на 3.11).

  • Windows: https://www.python.org/downloads/ — при установке обязательно отметьте «Add Python to PATH».
  • macOS: brew install python@3.11
  • Ubuntu/Debian: sudo apt update && sudo apt install python3 python3-venv python3-pip

Проверка:

python3 --version

6. Установка зависимостей

git clone <адрес-репозитория> botman
cd botman

python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

pip install -r requirements.txt

Состав зависимостей:

Пакет Зачем
Telethon клиент MTProto — тот же протокол, что у официальных приложений
python-dotenv чтение .env
SQLAlchemy[asyncio] слой хранения, переносимый с SQLite на PostgreSQL
aiosqlite асинхронный драйвер SQLite
cryptg (опционально) C-реализация шифрования, ускоряет скачивание в разы

Если cryptg не собирается (нет компилятора) — просто уберите его из requirements.txt, всё продолжит работать, только медленнее.

Почему Telethon

Сравнивались три варианта:

  • Telethon — активно поддерживается, зрелый, отличная документация, полный доступ к MTProto и «сырым» типам API, есть готовые события NewMessage, MessageEdited, MessageDeleted и raw-обновления. Выбран.
  • Pyrogram — хорошая библиотека, но оригинальный репозиторий давно без активных релизов; сообщество перешло на форк kurigram. Зависеть от форка в проекте, который должен просто работать годами, — лишний риск.
  • python-telegram-bot — это Bot API, а не клиентский. Бот не может читать вашу личную переписку и не видит удаления чужих сообщений. Для этой задачи не подходит принципиально.

7. Создание .env

cp .env.example .env

Откройте .env и заполните минимум три поля:

API_ID=1234567
API_HASH=0123456789abcdef0123456789abcdef
ARCHIVE_CHAT_ID=-1001234567890

Все остальные параметры имеют разумные значения по умолчанию и подробно прокомментированы прямо в .env.example. Наиболее полезные:

Переменная По умолчанию Значение
DATA_DIR data где лежат база, медиа и логи
DATABASE_URL пусто пусто = SQLite; для PostgreSQL — строка подключения
DOWNLOAD_MEDIA true скачивать ли файлы на диск
MAX_MEDIA_SIZE_MB 512 выше этого размера файл не качается
ARCHIVE_OUTGOING true архивировать ли ваши собственные сообщения
INCLUDE_CHATS пусто если задано — только эти чаты
EXCLUDE_CHATS пусто эти чаты не архивировать никогда
IMPORT_LIMIT_PER_CHAT 200 сколько сообщений на чат тянуть при импорте
ARCHIVE_SEND_DELAY 1.0 пауза между отправками в архивный чат
LOG_LEVEL INFO DEBUG для подробностей

.env уже добавлен в .gitignore. Не убирайте его оттуда.


8. Запуск

python main.py                  # постоянный мониторинг (основной режим)
python main.py --import         # импорт истории, затем выход
python main.py --import --monitor   # импорт, затем мониторинг
python main.py --check          # проверить конфиг, вход и архивный чат
python main.py --stats          # статистика архива (без подключения к Telegram)
python main.py --export-session # получить SESSION_STRING для сервера

Полезные флаги импорта:

python main.py --import --limit 1000            # 1000 сообщений на чат
python main.py --import --chats 5               # только первые 5 диалогов
python main.py --import --only-chat -1001234567890   # только этот чат

Первый запуск лучше начать с проверки:

python main.py --check

Вывод в консоли во время работы:

Connected as @username
Archive chat: My Archive (id -1001234567890)
Monitoring chats… (press Ctrl+C to stop)
18:30:02 INFO    Archived message #12345 from Иван (text)
18:30:03 INFO    Downloaded photo (1.2 MB) → media/photos/chat_555/12345_0_photo.jpg
18:30:41 INFO    Message #12345 edited — saved version 2 (previous version kept)
18:31:10 INFO    Archived deleted message #12345 from Иван (copy preserved)
18:35:00 INFO    FloodWait: waiting 32s before retrying send archive text

Остановка — Ctrl+C. Программа доработает очередь и корректно завершится.

Запуск с телефона (без компьютера)

Главное, что стоит понять сразу: телефон нужен только один раз — чтобы залогиниться в Telegram и получить SESSION_STRING. Дальше архиватор крутится на Railway круглосуточно, и телефон ему больше не нужен.

Держать сам архиватор на телефоне — плохая идея, и вот честная причина: Android убивает фоновые процессы при экономии батареи, Termux засыпает вместе с экраном, а iOS фоновый Python не даёт в принципе. Полнота архива напрямую зависит от непрерывности работы: всё, что удалят, пока программа спит, пропадёт навсегда. Поэтому телефон — для настройки, сервер — для работы.

Шаг 1. API_ID и API_HASH

Откройте https://my.telegram.org в браузере телефона (см. раздел 4). Мобильная версия сайта работает нормально.

Шаг 2. Получить SESSION_STRING

Нужен Python ровно на пять минут. Два варианта.

Вариант А — Google Colab. Работает и на Android, и на iPhone, ставить ничего не надо. Откройте https://colab.research.google.com, создайте новый блокнот и выполните одну ячейку:

!pip -q install telethon
from telethon import TelegramClient
from telethon.sessions import StringSession

api_id = int(input("API_ID: "))
api_hash = input("API_HASH: ")

client = TelegramClient(StringSession(), api_id, api_hash)
await client.start()          # спросит телефон, код и пароль 2FA
print("\nSESSION_STRING:\n" + client.session.save())

Colab поддерживает await на верхнем уровне, поэтому await client.start() работает как есть. Если версия среды вдруг ругается на await, добавьте в начало ячейки !pip -q install nest_asyncio и import nest_asyncio; nest_asyncio.apply().

⚠️ Скопируйте строку и удалите блокнот сразу после этого: Colab сохраняет вывод ячеек, а SESSION_STRING — это полный доступ к вашему аккаунту.

Вариант Б — Termux (только Android). Более «взрослый» путь, заодно можно всё потрогать локально:

pkg install python git
git clone https://github.com/kxronax/botman
cd botman
git checkout claude/telegram-personal-archiver-3iob11
pip install -r requirements.txt
cp .env.example .env
nano .env                      # вписать API_ID и API_HASH
python main.py --export-session

Если cryptg не соберётся (нужен компилятор) — либо pkg install clang, либо просто удалите эту строку из requirements.txt, всё будет работать и без неё.

Шаг 3. Узнать ARCHIVE_CHAT_ID с телефона

  1. Создайте в Telegram приватный канал (Новое сообщение → Создать канал → Частный канал), без других участников.
  2. Отправьте в него любое сообщение.
  3. Перешлите это сообщение боту @userinfobot — он ответит id канала.
  4. Убедитесь, что id начинается с -100. Если бот показал 1234567890 без минуса, значит нужный id — это -1001234567890.

Шаг 4. Railway через браузер телефона

  1. https://railway.appNew ProjectDeploy from GitHub repo → выбрать kxronax/botman.

  2. В настройках сервиса выбрать ветку claude/telegram-personal-archiver-3iob11 (или сначала смержить её в main).

  3. Variables → добавить:

    API_ID=...
    API_HASH=...
    SESSION_STRING=...     (из шага 2)
    ARCHIVE_CHAT_ID=-100...
    DATA_DIR=/data
    MAX_MEDIA_SIZE_MB=256
    
  4. Volume → mount path /data. Без этого база, медиа и сессия исчезают при каждом рестарте.

  5. Deploy. В логах должно появиться Connected as @… и Monitoring chats….

Первоначальный импорт истории с Railway запускается разово: временно поменяйте Start Command на python main.py --import --limit 500, дождитесь окончания в логах и верните обратно python main.py.


9. Авторизация

При первом запуске программа спросит:

  1. номер телефона в международном формате (или возьмёт из PHONE);
  2. код, который придёт в ваше приложение Telegram;
  3. пароль двухэтапной проверки (2FA), если он включён — ввод скрытый.

После этого создаётся файл сессии data/session/archiver.session, и код больше не спрашивается. Файл сессии — это полноценный доступ к аккаунту: храните его как пароль, не копируйте в публичные места, не коммитьте.

Если нужно «выйти» — удалите файл сессии и завершите сеанс в Telegram → Настройки → Устройства.


10. Настройка архивного чата

Заведите приватный канал (лучше всего) или группу, где вы единственный участник.

Как узнать её id:

Способ 1 — через саму программу. Запустите без ARCHIVE_CHAT_ID, отправьте любое сообщение в нужный чат, и в логе появится строка с его id.

Способ 2 — коротким скриптом:

python - <<'PY'
import asyncio, os
from dotenv import load_dotenv
from telethon import TelegramClient
load_dotenv()
async def main():
    async with TelegramClient("data/session/archiver", int(os.environ["API_ID"]),
                              os.environ["API_HASH"]) as client:
        async for d in client.iter_dialogs():
            print(f"{d.id:>20}  {d.name}")
asyncio.run(main())
PY

Скопируйте id в .env:

ARCHIVE_CHAT_ID=-1001234567890

У каналов и супергрупп id начинается с -100. У обычных групп — с -. У личных чатов — положительное число.

Важно: отправьте хотя бы одно сообщение в этот чат со своего аккаунта перед первым запуском — иначе Telegram может не отдать сущность чата клиенту, и программа сообщит об ошибке резолва.

Что приходит в архивный чат:

  • для текстового сообщения — одна запись формата [PRIVATE ARCHIVE];
  • для медиа — сам файл с короткой подписью, а следом полная карточка метаданных ответом на него (в подпись Telegram помещает только 1024 символа);
  • отдельные уведомления при редактировании и удалении.

Пример записи:

[PRIVATE ARCHIVE]

Chat: Иван Петров (@ivan) [private, id 555]
Sender: Иван Петров (@ivan) [id 555]
Date: 2026-09-02 18:30:00 UTC
Message ID: 12345
Chat ID: 555
Type: PHOTO
Direction: incoming

Media:
  - kind: photo
    file name: photo_987654321.jpg
    mime type: image/jpeg
    size: 200.0 KB (204800 bytes)
    telegram file id: 987654321
    dimensions: 1280x720
    saved to: media/photos/chat_555/12345_0_photo_987654321.jpg

Caption:
Смотри какой закат

Если ARCHIVE_CHAT_ID не задан — программа работает только с локальным архивом, это полностью поддерживаемый режим.


11. Первоначальный импорт

python main.py --import

Что происходит: программа обходит все доступные диалоги и тянет историю от новых сообщений к старым, по IMPORT_LIMIT_PER_CHAT штук на чат.

Особенности:

  • Возобновляемость. Прогресс по каждому чату пишется в таблицу import_state. Прервали на середине — следующий запуск продолжит с того же места, а не начнёт заново.
  • Дедупликация. Сообщение, уже сохранённое мониторингом, повторно не архивируется — уникальность гарантирует ограничение БД на (chat_id, message_id).
  • В архивный чат по умолчанию не шлётся (IMPORT_SEND_TO_ARCHIVE_CHAT=false): заливка тысяч старых сообщений в чат занимает часы и упирается в FloodWait. Локально при этом сохраняется всё.

Рекомендуемый порядок для большого аккаунта:

python main.py --import --limit 100    # быстрый проход по всем чатам
python main.py --import --limit 2000   # углубляемся, продолжая с места остановки
python main.py                         # дальше — мониторинг

Полная история: поставьте IMPORT_LIMIT_PER_CHAT=0. Будьте готовы, что на большом аккаунте это займёт много часов и десятки гигабайт.


12. Постоянный мониторинг

python main.py

Программа держит соединение, ловит события и архивирует всё в реальном времени.

Как оставить работать постоянно:

Linux, systemd — создайте /etc/systemd/system/tg-archiver.service:

[Unit]
Description=Personal Telegram Archiver
After=network-online.target

[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser/botman
ExecStart=/home/youruser/botman/.venv/bin/python main.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now tg-archiver
journalctl -u tg-archiver -f

macOS: launchd-plist с KeepAlive. Windows: «Планировщик заданий», триггер «При входе в систему».


13. Развёртывание на Railway

Проект пригоден для Railway, но есть два момента, о которых нужно знать заранее.

Ограничение 1: файловая система эфемерна

Контейнер Railway при каждом деплое и рестарте начинается с чистого диска. Без persistent storage вы потеряете и базу, и медиа, и файл сессии. Решение — Volume:

  1. В сервисе Railway → Variables → New Volume.
  2. Mount path: /data.
  3. Переменная DATA_DIR=/data.

Тогда база, медиа и сессия переживают рестарты.

Ограничение 2: интерактивный вход невозможен

На Railway некому ввести код из Telegram. Поэтому логинимся локально, а на сервер передаём готовую сессию строкой:

python main.py            # локально, проходим авторизацию
python main.py --export-session

Полученное значение положите в переменную Railway SESSION_STRING. Это секрет уровня пароля — только в Variables, никогда в git.

Порядок развёртывания

  1. Запушьте репозиторий на GitHub (.env и data/ исключены .gitignore).

  2. Railway → New Project → Deploy from GitHub repo.

  3. Добавьте Volume на /data.

  4. Задайте переменные окружения:

    API_ID=...
    API_HASH=...
    SESSION_STRING=...
    ARCHIVE_CHAT_ID=...
    DATA_DIR=/data
    MAX_MEDIA_SIZE_MB=256
    
  5. Стартовая команда уже описана в railway.json и Procfile: python main.py. Это worker, а не web-сервис: HTTP-порт не нужен, домен генерировать не надо.

  6. Политика рестарта — ALWAYS (задана в railway.json).

Ещё несколько практических моментов

  • Не запускайте одновременно локально и на Railway с одной сессией. Апдейты распределятся между двумя экземплярами, и часть сообщений не попадёт в архив. Выберите одно место.
  • Медиа съедает диск. Volume у Railway ограничен и платный. На сервере разумно поставить MAX_MEDIA_SIZE_MB=100…256, а то и DOWNLOAD_MEDIA=false, оставив только метаданные и пересылку файлов в архивный чат (сам Telegram при этом остаётся вашим файловым хранилищем).
  • PostgreSQL вместо SQLite. Railway даёт Postgres в один клик. Добавьте asyncpg в requirements.txt и задайте DATABASE_URL=postgresql+asyncpg://…. Обратите внимание: Railway выдаёт строку вида postgresql://…, схему нужно поменять на postgresql+asyncpg://. Медиафайлы всё равно требуют Volume — база их не хранит.
  • Часовые пояса. Всё время в архиве — UTC, независимо от хоста.

Если платить за Volume не хочется, честная альтернатива: держать архиватор на домашнем компьютере или дешёвом VPS, где диск свой.


14. Где находятся файлы

data/                          (или DATA_DIR)
├── database.sqlite3           метаданные: сообщения, версии, медиа, чаты
├── media/
│   ├── photos/chat_<id>/      фото
│   ├── videos/chat_<id>/      видео, видеосообщения, GIF
│   ├── documents/chat_<id>/   документы
│   ├── audio/chat_<id>/       аудио и голосовые
│   ├── stickers/chat_<id>/    стикеры
│   └── other/chat_<id>/       всё остальное
├── logs/
│   └── archiver.log           лог с ротацией (5 × 10 МБ)
└── session/
    └── archiver.session       файл сессии — СЕКРЕТ

Имя медиафайла: <message_id>_<индекс>_<исходное имя>.

Таблицы базы: messages, message_versions, media_files, chats, senders, deletion_events, import_state, app_state.

Посмотреть архив вручную:

sqlite3 data/database.sqlite3 \
  "SELECT message_id, date, sender_name, substr(text,1,60) FROM messages ORDER BY date DESC LIMIT 20;"

15. Восстановление после сбоя

Программа спроектирована так, чтобы переживать сбои без ручного вмешательства.

Ситуация Что происходит
Обрыв интернета Telethon переподключается сам, бесконечно, с задержкой 5 с
Временная ошибка Telegram повтор с экспоненциальной задержкой (2, 4, 8, 16 с)
FloodWait ожидание ровно указанного времени, затем повтор; в лог — INFO
FloodWait > 1 часа задание помечается failed и повторится позже, процесс не блокируется
Ошибка скачивания файла статус failed в media_files, повтор при следующем проходе (до 5 попыток)
Частично скачанный файл скачивание идёт в .part, переименование только после успеха; «хвосты» удаляются при старте
Ошибка отправки в архивный чат archive_status='failed', повтор при следующем запуске
Убили процесс / перезагрузка всё состояние в БД; при старте незавершённые задания ставятся в очередь заново
Прерванный импорт продолжается с сохранённой точки в import_state

Просто перезапустите программу — она сама подберёт незаконченную работу:

python main.py

Проверить состояние:

python main.py --stats
tail -f data/logs/archiver.log

Если база повреждена (жёсткое отключение питания):

sqlite3 data/database.sqlite3 "PRAGMA integrity_check;"

Резервная копия — обычное копирование каталога data/ при остановленной программе (или sqlite3 data/database.sqlite3 ".backup data/backup.sqlite3" на работающей).


16. Обновление зависимостей

source .venv/bin/activate
pip install --upgrade -r requirements.txt

Обновлять Telethon стоит регулярно: Telegram меняет схему API (layer), и старые версии перестают понимать новые типы сообщений — они начнут попадать в архив как unsupported.

Проверить, что обновление ничего не сломало:

python tests/smoke_test.py     # 43 проверки, Telegram не требуется
python main.py --check         # проверка конфига и доступов

Зафиксировать текущий набор версий:

pip freeze > requirements.lock.txt

17. Архитектура

botman/
├── main.py                     тонкий launcher
├── app/
│   ├── main.py                 разбор аргументов, режимы, жизненный цикл
│   ├── config.py               конфиг из окружения, фильтры чатов
│   ├── logging_setup.py        логирование + фильтр секретов
│   ├── telegram/
│   │   ├── client.py           создание клиента, авторизация, 2FA
│   │   ├── handlers.py         события: новое / изменено / удалено / реакции
│   │   ├── extract.py          Telethon-объект → структурированные метаданные
│   │   ├── media.py            скачивание файлов, политика view-once
│   │   └── importer.py         импорт истории с возобновлением
│   ├── archive/
│   │   ├── models.py           схема БД (SQLAlchemy)
│   │   ├── database.py         движок, сессии, PRAGMA для SQLite
│   │   ├── repository.py       весь доступ к данным
│   │   ├── storage.py          раскладка файлов, атомарные скачивания
│   │   ├── formatter.py        текст записей для архивного чата
│   │   ├── sender.py           отправка в архивный чат
│   │   └── pipeline.py         оркестратор: захват → очередь → обработка
│   └── utils/
│       ├── jsonutil.py         сериализация «сырых» объектов API
│       ├── ratelimit.py        FloodWait и повторы
│       └── text.py             хеши, имена файлов, разбиение текста
└── tests/smoke_test.py         сквозной тест без сети

Ключевое решение: захват и обработка разделены

событие ──► capture()          ── запись в БД, миллисекунды
                │                 (поэтому удаление через 2 с не страшно)
                └─► очередь ──► worker ── скачивание медиа
                                        └─ отправка в архивный чат
                                        └─ статус в БД, повтор при сбое

Обработчик события делает только запись в базу и кладёт задание в очередь. Всё медленное — скачивание файлов, загрузка в архивный чат, ожидание FloodWait — выполняется отдельным воркером. Если бы всё делалось прямо в обработчике, скачивание 200-мегабайтного видео заблокировало бы приём остальных сообщений, и удалённые сообщения терялись бы именно тогда, когда они важнее всего.

Состояние очереди хранится в БД (archive_status, download_status), а не в памяти, поэтому перезапуск ничего не теряет.

Дедупликация

На уровне схемы: UNIQUE (chat_id, message_id). Даже гонка между импортом и живым обработчиком не создаст дубль — вторая вставка получит IntegrityError и вернёт существующую запись. Дополнительно для файлов считается SHA-256, что позволяет находить идентичное содержимое (repository.find_duplicate_media).

Переход на PostgreSQL

Используются только переносимые типы колонок и никаких диалект-специфичных UPSERT. Достаточно поставить asyncpg и задать DATABASE_URL — схема создастся сама. Для переноса существующих данных подойдёт pgloader.


18. Безопасность

  • Все секреты — только в переменных окружения. В коде нет ни одного захардкоженного значения.
  • .env, data/ и *.session в .gitignore.
  • api_hash и SESSION_STRING никогда не попадают в лог: за этим следит SecretRedactingFilter, который вырезает их из любой строки лога, даже если их залогируют по ошибке. Пароли ввода 2FA читаются через getpass и нигде не сохраняются.
  • Каталог сессии создаётся с правами 0700.
  • Данные уходят только в Telegram (ваш архивный чат) и на локальный диск. Никакой аналитики, телеметрии, трекеров и сторонних API — исходящих HTTP-вызовов в проекте нет вообще, кроме соединения с серверами Telegram.
  • Зависимостей минимум, все — известные и широко используемые.

Рекомендация: если архив лежит на ноутбуке, включите шифрование диска (FileVault / BitLocker / LUKS). База и медиа хранятся в открытом виде.


19. Что можно добавить дальше

Архитектура намеренно оставляет для этого место:

  • веб-панель — читающая ту же базу через repository.py (FastAPI + любой фронт);
  • полнотекстовый поиск — таблица FTS5 в SQLite или tsvector в PostgreSQL;
  • фильтрация и статистика — запросы поверх готовой схемы;
  • экспорт — HTML/JSON/PDF выгрузка из messages + media_files;
  • резервное копирование — периодический .backup и синхронизация media/;
  • PostgreSQL — уже поддержан, нужен только драйвер и URL;
  • DockerDockerfile поверх python:3.11-slim и volume на /data;
  • дедупликация файлов по SHA-256 — хеши уже считаются, осталось делать hard-link вместо копии;
  • уведомления — например, отдельное сообщение при удалении важного диалога;
  • шифрование архива на диске — прозрачный слой поверх storage.py.

Ничего из этого не требует переписывать существующий код: слой Telegram и слой хранения уже разделены.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages