telegram-snapshot
Skill9 starsChanged 3 months agoMITView on GitHub
Настроить или починить инкрементальный pull Telegram-чатов на устройстве: \"my.telegram.org выдает ERROR\", \"telethon не видит чаты\". НЕ для отправки сообщений - скилл `telegram-send`.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- dewil/ai-toolkitword for word, first seen Aug 25, 2026
Other versions1 project uses a version of this.
Word for word · 1
- dewil/ai-toolkit★ 9100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository37 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- agent-browserSkill
- android-apk-releaseSkill
- asana-blockersSkill
- asana-commentsSkill
- asana-projectSkill
- backlog-taskSkill
- canon-syncSkill
- csv-xlsxSkill
- estimate-calibrationSkill
- gdocsSkill
- harvest-canonSkill
- implementSkill
- jev-decideSkill
- llm-ruSkill
- local-transcriptionSkill
- md-docxSkill
- md-pdfSkill
- md-pptxSkill
- memory-compactSkill
- memory-searchSkill
- migrateSkill
- minimal-codeSkill
- mymeet-snapshotSkill
- office-mdSkill
- pii-maskSkill
- proofreadSkill
- qa-browserSkill
- redmine-snapshotSkill
- repo-publicationSkill
- requirementsSkill
- reviewSkill
- sddSkill
- session-costSkill
- telegram-sendSkill
- tutorSkill
- web-researchSkill
The file
Files
telegram-snapshot
Скилл для первой настройки или починки автоматизированного pull Telegram-чатов проекта через Telethon. Фиксирует накопленный опыт по граблям, на которые натыкаешься в первый раз.
Скрипты-эталоны лежат в каноне в scripts/telegram-snapshot.py и scripts/telegram-deltas.py (top-level папка scripts/, не внутри папки скилла). .AI bootstrap типа management копирует их в scripts/ в корне проекта - оттуда же, где их ожидает скилл при запуске (python3 scripts/telegram-snapshot.py). В эталонной версии уже есть iter_dialogs() прогрев (см. шаг 5).
Когда применять
- Пользователь впервые настраивает скрипты
scripts/telegram-snapshot.py/telegram-deltas.pyна новом устройстве. - my.telegram.org возвращает голое
ERROR.при создании приложения и не дает api_id/api_hash. - Telethon ругается
Could not find the input entity for PeerUser(...)на свежей сессии. - Сессия отозвана (
AuthKeyUnregisteredError), нужно перелогиниться. - Подключается новый проект - нужно создать
.telegram-snapshot.jsonи проверить, что общая авторизация на устройстве уже есть.
Когда НЕ применять
- Обычный регулярный pull (запуск
telegram-snapshot.pyдля свежих сообщений) - это просто Bash-команда из шаблона дейлика, отдельный скилл не нужен. - Любые задачи по экспорту/анализу самих сообщений - они уже в
result.json, читай напрямую.
Алгоритм
Шаг 1. Проверить общую авторизацию устройства
ls ~/.config/telegram-snapshot/Должно быть:
auth.json(api_id, api_hash, session_name) - права 600.<session_name>.session- SQLite-сессия Telethon.
Если файлов нет - идем к шагу 2. Если auth.json есть, а .session нет - идем к шагу 4 (первый интерактивный логин).
Шаг 2. Получить api_id и api_hash
Сначала пробовать свои через my.telegram.org.
Если my.telegram.org возвращает ERROR. при "Create application" - это блокировка на стороне аккаунта (антифрод по новизне аккаунта и/или IP VPN). По состоянию на май 2026 воспроизведено на разных комбинациях полей формы (platform=desktop/other, разные title/shortname, с url и без) - сервер режет одинаково. Лечится:
- Сменой VPN-узла на другую страну/провайдера (см. список ниже).
- Ожиданием 6-24 часа после неудачных попыток.
- Использованием другого, более старого активного Telegram-аккаунта.
VPN-предпочтения для my.telegram.org: лучше Германия/Нидерланды/Финляндия, хуже США/UK. Резиденциальные IP > datacenter. Свой WireGuard на личном VPS почти всегда проходит.
Fallback - публичные ключи Telegram Desktop OSS:
api_id:123456api_hash:YOUR_API_HASH
Это публичные значения из исходников tdesktop. Технически серая зона: ToS Telegram требует получать собственный api_id, гарантий не давать банов нет. В личной некоммерческой практике банов не наблюдалось, но ответственность на пользователе - надежнее получить свой api_id (см. жесткое правило 4).
Шаг 3. Заполнить auth.json
mkdir -p ~/.config/telegram-snapshot
chmod 700 ~/.config/telegram-snapshotСодержимое ~/.config/telegram-snapshot/auth.json (выставить права 600):
{
"api_id": 123456,
"api_hash": "YOUR_API_HASH",
"session_name": "<имя_аккаунта>"
}session_name - произвольное (например dwl), под этим именем будет файл <name>.session в той же папке.
Опциональное поле proxy - для хостов, где прямой доступ к Telegram режется (см. "Запуск с сервера / прокси").
Шаг 4. Поставить telethon и сделать первый запуск
pip3 install --user telethon
cd <project-root>
python3 scripts/telegram-snapshot.pyПервый запуск обязательно интерактивный - в Bash из этого скилла НЕ запускать (stdin недоступен). Сказать пользователю запустить в его терминале и продиктовать ввод:
- Phone:
+7...(международный формат). - Code: из приложения Telegram (приходит в Saved Messages от "Telegram").
- Password: 2FA, если включена.
После успеха появится ~/.config/telegram-snapshot/<session_name>.session. Дальше запуски без ввода - в любом проекте на этом устройстве.
Шаг 5. Лечение типичных ошибок при первом pull
Could not find the input entity for PeerUser(...) на нормальных групповых чатах. На свежей сессии telethon без подсказки трактует голый int chat_id как user_id. Лечится прогревом - перед циклом обработки чатов в amain() нужно пройтись по диалогам через client.iter_dialogs(). В каноне это совмещено с построением карты {unmarked_id -> entity}:
dialog_entities = {}
async for d in client.iter_dialogs():
eid = getattr(d.entity, "id", None)
if eid is not None:
dialog_entities[eid] = d.entityКарта нужна и для прогрева, и как авторитетный источник entity (см. следующий пункт про битый кеш). В канонической версии scripts/telegram-snapshot.py этот блок уже есть. Если в проекте старая версия скрипта без прогрева iter_dialogs() - обновить клиент с .AI через /canon и canon-sync; legacy-only клиент сначала требует отдельного запроса на миграцию в .AI.
Один и тот же chat_id резолвится в РАЗНЫЕ чаты между запусками (битый entity-cache). На некоторых сессиях get_entity(<голый int>) нестабилен: локальный кеш entity повреждается и тот же id отдается то одним, то другим чатом. Симптом - снапшоты "уезжают" не в свои папки. Лечится тем, что entity берется не из get_entity, а из свежего серверного списка диалогов: в amain() цикл iter_dialogs() строит карту {unmarked_id -> entity}, и process_chat резолвит чат через нее (resolve_entity), а get_entity оставлен только фолбэком для чатов вне списка диалогов. В канонической версии это уже сделано. При сомнениях, что метки папок верны, сверяй заголовки чатов через iter_dialogs, а не доверяй get_entity.
AuthKeyUnregisteredError / сессия отозвана. Удалить ~/.config/telegram-snapshot/<session_name>.session и перезапустить - пройдет авторизация с нуля.
Server closed the connection: 0 bytes read on a total of 8 expected bytes на старте. Безобидное, telethon просто перезаключает соединение, продолжает дальше. Если повторяется на каждом ивенте - проблема с VPN, попробовать сменить узел.
Шаг 6. Подключение нового проекта
В корне нового проекта создать .telegram-snapshot.json:
{
"chats_root": "<путь вне синка>/2026-08 Клиент",
"chats": {
"с заказчиком": 4715985727,
"с командой": 2644101635,
"1-1/Иванов И.И.": 123456789,
"бот": {"id": 4715985727, "topic_id": 30127}
}
}chats_root держится вне синкаемой папки проекта. Зеркало чата - техническое: личная переписка легко дает сотни мегабайт, которые иначе уезжают на все устройства пользователя и в облачные бэкапы (rules/docs-maintenance.md, "Технические артефакты в синкаемой папке"). Путь может быть абсолютным - это и есть штатный способ. Ключ не задан - скрипт берет хранилище устройства: $TELEGRAM_SNAPSHOT_STORE или ~/.local/share/telegram-snapshot/chats/<имя папки проекта>-<хеш пути> (хеш нужен, чтобы два проекта с одинаковым именем папки не делили одно хранилище). Переменную задавайте на устройстве - в системном окружении, а не для одной команды: снапшот и дельты вычисляют путь независимо, и переменная, видимая только одному из них, разведет их по разным папкам, после чего дельты молча покажут ноль изменений. Надежнее прописать chats_root в конфиге явно. Единственное исключение - проект, заведенный до 23.08.2026: если папка Встречи/чаты в нем уже есть, она продолжает использоваться (иначе история качалась бы заново в новое место, а старая копия так и осталась бы в синке), и скрипт печатает команду переноса. Зеркало, оказавшееся внутри проекта, скрипт не запрещает, но предупреждает на каждом прогоне.
Медиа-кэш. Вложения всегда идут мимо проекта, в ~/.cache/telegram-snapshot/media, и чистятся по TTL (по умолчанию 168 часов - неделя). Оба значения перекрываются: переменными TELEGRAM_SNAPSHOT_MEDIA / TELEGRAM_SNAPSHOT_MEDIA_TTL на устройстве или ключами media_cache / media_ttl_hours в проектном конфиге. Правка констант в самом скрипте не переживет /canon - копий на машине много.
chat_id для уже-экспортированного чата берется из result.json (поле id в шапке). Для чата, которого еще нет на диске, скрипт сделает bootstrap - создаст шапку и подтянет всю историю через Telethon (см. ниже).
Форумные темы (topic_id). Запись чата бывает в двух формах: короткая "label": <chat_id> (весь чат) и расширенная "label": {"id": <chat_id>, "topic_id": <id>} - для форум-чатов, когда в дельты нужна только одна тема (например, топик бота среди прочих рабочих топиков). Фильтр по topic_id применяется только в telegram-deltas.py; telegram-snapshot.py всегда тянет чат целиком (темы лежат в шапке topics[], у сообщений проставлен topic_id) - так весь контекст чата сохраняется, а ежедневная сводка не шумит чужими темами. topic_id - это id корневого сообщения темы; узнать проще всего после первой выкачки из result.json -> topics[] (там id + title).
Чат подпроекта (dest). Необязательный ключ расширенной записи: "Лариса CV": {"id": 464543113, "dest": "2026-03 CV Лариса/чаты/Лариса"} - путь папки чата от корня проекта, вместо chats_root/<label>. Нужен в топологии клиент-зонтика (rules/client-umbrella.md): зеркало чата подпроекта живет в папке подпроекта, но чат при этом участвует в регулярном снапшоте и дельтах наравне с остальными (симметрично dest у mymeet-snapshot). Абсолютный путь разрешен (тот же довод, что у chats_root выше); относительный обязан остаться внутри проекта - выход через .. скрипт отклоняет как опечатку; дубли целевых папок (два чата в одну, в том числе различающиеся только регистром) и папку с зеркалом другого чата он отклоняет тоже. Без dest поведение прежнее.
Чат без вложений (media). Необязательный ключ расширенной записи: "комьюнити": {"id": 1539618878, "media": false} - тянуть только текст и метаданные, вложения не скачивать (метаданные файлов - имя, размер, mime - в сообщениях остаются). Нужен для чатов с потоком картинок - мем-флудилки, новостные каналы: полезной нагрузки во вложениях нет, а скачивание тысяч файлов роняет прогон разрывом соединения (симптом в логе - Server closed the connection и CancelledError из download_media; это не "телеграм недоступен"). По умолчанию true - для рабочих чатов вложения нужны, и старые конфиги читаются без правок. У telegram-pull-one.py то же выключается флагом --no-media. Выключение необратимо для уже забранных сообщений: вложения скачиваются только у новых, докачки задним числом нет - вернув media: true, файлы пропущенного периода не получить. Окно правок и удалений (ниже) этого не меняет: в нем перечитываются текст и метаданные вложения, но сам файл не скачивается ни при media: true, ни при media: false - и ось "media" от оси "окно правок" не зависит, текстовые правки в чате без вложений подхватываются так же.
Конвенция подпапок. Label интерпретируется буквально как путь внутри chats_root: result.json для label с заказчиком лежит в <chats_root>/с заказчиком/, а для label 1-1/Иванов И.И. - в <chats_root>/1-1/Иванов И.И./. Префикс 1-1/ имеет особый смысл - персональные чаты (личные диалоги с участниками 1-1-встреч). telegram-deltas.py выводит их отдельным блоком #### 1-1 под общим списком групповых чатов, со срезанным префиксом в display name. Прочие подпапки скрипт просто проксирует как часть пути, без специальной обработки.
Сами скрипты scripts/telegram-snapshot.py и scripts/telegram-deltas.py приходят из канона при bootstrap .AI для типа management и при последующем canon-sync обновлении. Не пересобирать руками из других проектов - старые копии могут не содержать iter_dialogs() прогрев и режим миграции legacy формата.
Шаг 7. Формат result.json и режимы скрипта
Канонический формат result.json - расширенный Telegram Desktop export:
{
"name": "<имя чата>",
"type": "private_supergroup",
"id": 2644101635,
"topics": [
{ "id": 167, "title": "Маршруты", "date": "...", "date_unixtime": "...", "icon_color": 13338331 }
],
"messages": [
{
"id": 168, "type": "message",
"date": "...", "date_unixtime": "...",
"from": "...", "from_id": "user...",
"text": "...", "text_entities": [...],
"topic_id": 167,
"reply_to_message_id": 200,
"edited": "...", "edited_unixtime": "..."
}
]
}Дополнения относительно сырого TG Desktop экспорта:
topics: []в шапке - метаданные форумных тем (id,title,date,date_unixtime,icon_color). При переименовании топика обновляетсяtitle.topic_idу сообщения - id корневого сообщения форум-темы. Заполняется только для сообщений в форум-чатах с топиками.reply_to_message_idостается только для настоящих реплаев (когда пользователь ответил на конкретное сообщение внутри топика). "Цепляние к шапке топика" больше не выражается через reply - оно вtopic_id.edited/edited_unixtime- если сообщение было отредактировано в TG.topic_created/topic_editedservice-сообщения вmessages[]не сохраняются - вся инфа о топиках вtopics[].
Скрипт telegram-snapshot.py работает в трех режимах:
- Bootstrap.
result.jsonотсутствует -> создается шапка черезresolve_entity()(entity берется из карты диалоговiter_dialogs,get_entity- только фолбэк для чатов вне списка диалогов), история целиком подтягивается сmin_id=0. Удобно для проектов, где нет ручного TG Desktop экспорта. - Migration.
result.jsonесть, но в формате сырого TG Desktop экспорта (безtopics[]в шапке). Скрипт:- сохраняет копию в
result.pre-migration.json(бэкап перезаписывается, если миграция запускается повторно); - извлекает
topic_created/topic_editedизmessages[]вtopics[]; - проставляет
topic_idсообщениям, поднимаясь по цепочкеreply_to_message_idдо сообщения, чей id есть в множестве корней тем; - очищает
reply_to_message_id, если он указывал ровно на корень темы (теперь это выражается черезtopic_id); - удаляет
topic_created/topic_editedservice-сообщения изmessages[]. - При ошибке миграции (поломанный JSON, неполные данные) - файл не трогается, чат пропускается, бэкап остается.
- сохраняет копию в
- Incremental pull. Стандартный режим: тянем сообщения с
id > max(messages.id, topics.id)(high-water mark учитывает оба массива - иначе service-only хвост после последнего обычного сообщения тянулся бы каждый запуск), апдейтимtopics[], дописываем вmessages[]. Если есть реальные новые сообщения/топики/правки - сохраняемresult.prev.jsonкак baseline до них (дляtelegram-deltas.py); если миграция случилась без новых сообщений -result.prev.jsonне трогаем, дельта будет посчитана при следующем pull с реальными новыми. Записьresult.jsonиresult.prev.jsonатомарная - через*.tmp+os.replace, чтобы Ctrl+C / OOM не оставили битый JSON. Отдельно от инкремента перечитывается окно последних сообщений ради правок и удалений - см. "Правки и удаления" ниже; правки и удаления сохраняютresult.json, но baselineresult.prev.jsonне двигают: дельты считаются по новымid.
Все три режима могут сработать в одном запуске: для разных чатов из конфига - разное состояние. Скрипт принимает решение по каждому чату отдельно.
Починка старых миграций (внутри incremental pull). На уже мигрированном снапшоте скрипт дополнительно прогоняет repair_legacy_migration(): достраивает topic_id сообщениям, потерявшим его из-за старого бага миграции (reply-on-reply внутри темы). Если что-то восстановлено - один раз сохраняет бэкап result.pre-repair.json. На здоровом снапшоте это no-op. Отдельным режимом не считается - срабатывает автоматически при обычном pull.
Известные ограничения:
- Правки и удаления - только в окне последних 50 сообщений. Инкремент по
id > high_waterих не видит, поэтому каждый прогон дополнительно перечитывает последниеEDIT_WINDOWсообщений (50, переменнаяTELEGRAM_SNAPSHOT_EDIT_WINDOW): отредактированное обновляется, прежний текст уходит вedit_history, удаленное помечаетсяdeleted: trueи из зеркала не стирается. Счетчикиправок: K, удалений: Mпечатаются всегда, нулевые тоже - иначе "проверил, правок нет" не отличить от "не проверял" (silent-failure.md). Правка сообщения глубже окна в зеркало не попадет: там по-прежнему только bootstrap заново. Чат короче окна сверяется целиком, включая удаление первых сообщений. Что окно не делает: замененное вложение получает новые метаданные, но файл заново не скачивается; дельты (telegram-deltas.py) считаются по новымid- правку старого сообщения они не покажут, удаленное после baseline в "новое" не попадает. Второй запрос упал (сеть,FloodWait) - забранные новые сообщения все равно записываются, а в строке прогона вместо счетчиков стоит "правки не проверены": это тоже отличимое состояние, а не тишина. Прецедент, ради которого это появилось: HR, 18.08.2026 - собеседница задним числом переписала сообщение, три прогона ответили "новых нет". - Bootstrap не делает checkpoint. Долгий первый pull многотысячного чата при сбое (Ctrl+C, network) начнется заново - частично сохраненного
result.jsonне будет, потому что атомарная запись делается только в конце. Это компромисс простоты против устойчивости. - Migration по reply-цепочке имеет лимит.
migrate_legacyопределяетtopic_idобычного сообщения, поднимаясь от егоreply_to_message_idдо сообщения, чей id есть в множестве корней тем. Если цепочка обрывается (reply на сообщение, которого нет в экспорте - например, удалено) -topic_idне проставится. Контент сообщения не теряется, только связь с темой.
Медиа-кэш вложений. Вложения новых сообщений (фото, документы) скачиваются в локальный НЕсинкаемый кэш ~/.cache/telegram-snapshot/media/<chat_id>/, имя файла <msg_id>_<оригинал|расширение>. Агент читает их сам: скрин - визуально через Read, файл - сканером. Ключевые свойства:
- Кэш вне проекта - принципиально. Папки проекта часто синкаются в облако, а вложения бывают с секретами; кэш вне синка - секреты в облако не текут.
result.jsonне меняется: ни байтов, ни путей медиа туда не пишется. Корреляция сообщение<->файл по<msg_id>в имени файла кэша + метаданныеfile_name/mimeв самой записи сообщения.- TTL 24ч: на каждом запуске чистятся файлы кэша старше суток (по mtime). Прочитал -> сутки доступно -> подчистилось.
- Превью ссылок (webpage) вложением не считаются. Падение скачивания одного файла по обычной ошибке pull не роняет (best-effort); а вот массовый поток вложений способен уронить обработку чата целиком разрывом соединения (
CancelledError) - для таких чатов естьmedia: false(см. "Чат без вложений").
Шаг 8. Апгрейд скриптов в существующем проекте
Скрипты эволюционируют (новые поля, новые режимы, фиксы). В management-проекте с .AI обновление выполняется закрепленной процедурой через /canon и canon-sync; она проверяет конфликты и сохраняет проектные данные. Для legacy-only клиента переход в .AI сначала нужно отдельно запросить через migrate. Не копируй скрипты вручную из других проектов - они могут расходиться по версии.
Несколько аккаунтов
Если переписка ведется с двух номеров (рабочий и, например, отдельный для откликов), контур работает с обоими: аккаунты описываются в auth.json, а каждый чат помечается своим.
1. Аккаунты в auth.json - секция accounts, имя аккаунта -> его настройки:
{
"api_id": 123456,
"api_hash": "YOUR_API_HASH",
"accounts": {
"default": {},
"cv": {}
}
}Ключи верхнего уровня (api_id, api_hash, proxy) наследуются каждым аккаунтом - одно приложение на несколько номеров - и при необходимости переопределяются внутри аккаунта.
session_name - исключение: он НЕ наследуется. По умолчанию он равен имени аккаунта, поэтому у каждого свой <имя>.session. Это важно при миграции: если в старом плоском конфиге был задан session_name, а сверху добавили accounts, наследование посадило бы все аккаунты на один session-файл - то есть на одну авторизованную сессию, и никакой изоляции бы не было. Задать аккаунту свое имя сессии можно явно, внутри его секции; если два аккаунта укажут одно и то же, скрипт откажется работать и скажет, какие именно столкнулись.
Старый плоский формат (поля в корне, без accounts) продолжает читаться как единственный аккаунт default - существующие проекты менять не нужно.
2. Аккаунт у чата в .telegram-snapshot.json - расширенная запись с полем account (без него подразумевается default):
{
"chats": {
"с командой": 4715985727,
"рекрутер X": {"id": 123456789, "account": "cv"}
}
}Snapshot группирует чаты по аккаунту и обходит аккаунты последовательно, каждый своей сессией.
3. Первый логин нового аккаунта - только через snapshot. telegram-snapshot.py использует интерактивный client.start() (спросит номер и код), а telegram-send.py намеренно неинтерактивен - он лишь проверит авторизацию и откажет. Поэтому порядок: добавить аккаунт в auth.json, пометить им чат, один раз прогнать snapshot и залогиниться - после этого работает и отправка.
4. Драйверы по id берут аккаунт флагом: telegram-pull-one.py <id> <путь> --account cv, telegram-send-one.py <id> --account cv --text "...".
Типовая ошибка. Чат не резолвится ("не удалось найти чат") или резолвится в чужой - первым делом проверь поле account у этого чата: чат виден только той сессии, которой принадлежит, и карта диалогов у каждого аккаунта своя.
telegram-deltas.py про аккаунты не знает и знать не должен - он работает с уже выкачанным JSON, сессия ему не нужна.
Single-chat-by-id: pull одного чата вне конфига
Для сценариев, где чат в общий конфиг класть нельзя (разовый чат, клиент трека поддержки - не должен попадать в ежедневные pull/дельты), есть драйвер scripts/telegram-pull-one.py: инкрементальный pull ОДНОГО чата по id в произвольную папку, минуя .telegram-snapshot.json:
python3 scripts/telegram-pull-one.py <chat_id> <out_path> [expected_username]out_path - абсолютный путь (штатно - в хранилище зеркал вне синка) либо путь от корня проекта; во втором случае скрипт предупреждает до выкачки, что зеркало окажется в синкаемой папке. Результат в <out_path>/result.json (тот же формат, тот же инкремент, тот же медиа-кэш - вся логика переиспользуется из telegram-snapshot.py через импорт). Опциональный expected_username - страховка: если username чата не совпал или отсутствует, pull прерывается с кодом 2 до выкачки, чтобы не выкачать чужой чат. Явно переданный пустой expected_username - ошибка ввода с кодом 2 до создания клиента. Код совпадает с отказом по username в telegram-send-one.py. Симметричная отправка по id - scripts/telegram-send-one.py (см. скилл telegram-send).
Запуск с сервера / прокси
С хостов, где прямой доступ к Telegram API режется DPI (типично для RU-датацентров), telethon на каждом коннекте падает с Attempt N at connecting failed: TimeoutError. Env-прокси (HTTP_PROXY/ALL_PROXY) telethon не читает - прокси задается полем proxy в ~/.config/telegram-snapshot/auth.json этого устройства:
{ "api_id": ..., "api_hash": "...", "session_name": "...", "proxy": "socks5://127.0.0.1:7890" }- Формат -
scheme://host:port. Для socks-схем нужен пакет:pip3 install --user "python-socks[asyncio]". Нет поля - прямое подключение, поведение прежнее. - Настройка per-device: ноутбук ходит напрямую (без поля), серверная VM - через свой локальный прокси.
- На сервере - отдельное
session_nameи свой интерактивный первый запуск: одновременная работа одного.sessionс двух IP кончаетсяAuthKeyUnregisteredError/ конфликтом auth-ключа. - Поле читают все клиентские скрипты:
telegram-snapshot.py,telegram-pull-one.py,telegram-send-one.py,telegram-send.py(хелперclient_kwargs).
Общая сессия: database is locked
Авторизация одна на устройство (auth.json + .session), а .session - это sqlite. Пока с ней работает один процесс, остальные получают изнутри telethon:
sqlite3.OperationalError: database is lockedТипичный источник - параллельная сессия Claude из другого проекта, запустившая свой telegram-snapshot.py: сессия у них общая с нашей. Падает любой клиентский скрипт, обычно на подключении (client.connect() / client.start() -> sessions/sqlite.py).
Это не поломка и не зависший процесс. Чужой прогон дорабатывает сам и отпускает сессию - как правило в пределах 30-60 секунд.
Скрипты закрывают подключение сами: оно идет через connect_with_retry (5 попыток с паузой 15 секунд, повтор только на database is locked; sqlite ждет еще до 5 секунд внутри каждой попытки и столько же в закрытии между попытками, то есть на живом локе до отказа проходит около двух минут). Закрытие клиента тоже безопасно - disconnect_quietly не роняет прогон своей ошибкой. Не покрыто ретраем открытие сессии в конструкторе TelegramClient (редкий случай: лок ровно на чтении схемы) - там ошибка прилетит сразу, без ожидания.
Лок на disconnect - не провал. Строка disconnect не отработал (OperationalError: database is locked) сама по себе означает лишь, что не закрылось соединение, - судить по ней об исходе прогона нельзя ни в какую сторону (она бывает и в неудачной попытке подключения). Смотреть надо на итог.
Отсюда правило для агентских оберток и крон-промптов: не считать попытку неудачной по одному совпадению строки "database is locked" в выводе. Признак успеха прогона - итоговая строка OK: (OK: +N сообщений при заборе, OK: отправлено при отправке) и нулевой код возврата: OK: означает, что прогон штатно завершен - данные забраны (и записаны, если было что писать; +0 без новых сообщений - тоже штатный исход), а при отправке сообщение принято сервером, - что бы ни писалось про lock выше. Обертка вида grep -qi "database is locked" && retry без этой проверки прячет свежие сообщения (скрипт их уже записал, а обертка доложила "проверить не удалось") - на отправке тот же паттерн еще и провоцирует дубль сообщения живому получателю. Два уточнения:
- При частичном отказе (упал хотя бы один чат) снапшот печатает не
OK:, аЧАСТИЧНО: +N сообщений; провалено чатов: K (<labels>)и возвращает код 1; упавшие чаты видны и построчно (!! <label>, "чат пропущен"). Успешные чаты при этом уже записаны - обертка ретраит только упавшие, а не весь прогон. - Для отправки правило действует, только когда
OK: отправленонапечатан. Лок, прилетевший ДО него (в кадреsend_message/send_file), - отдельный случай "статус неизвестен, повтор опасен": см. скиллtelegram-send, раздел проdatabase is locked.
Что делать, когда ошибка все же дошла:
- Чужой процесс не убивать.
pgrep -af "telegram-(snapshot|pull-one|send)"уместен только чтобы убедиться, что это чужая штатная работа.kill/pkillпо нему рвет выкачку другого проекта на середине. - Не "лечить" сессию. Удаление
.session-journalили самого.session- это сброс авторизации устройства, а не исправление блокировки. - Не эскалировать в починку. Отложить прогон (следующий тик расписания, позже по просьбе пользователя) и сказать пользователю, что сессию держит другой проект. Ручной повторный запуск через минуту-другую - тоже нормально.
- Повтор снапшота безопасен, повтор отправки - нет. Pull инкрементальный и идемпотентный. Отправка - необратимое внешнее действие, там свои правила (см. скилл
telegram-send). - В мульти-аккаунтном прогоне лок обрывает весь снапшот, а не только свой аккаунт: аккаунты обходятся последовательно, и необработанные останутся до следующего запуска (в отличие от ошибки по одному чату - та печатается и не мешает остальным).
- Ретрай ставится только на подключение. Если добавляешь новую точку подключения - через
connect_with_retry. Оборачивать в ретрай саму отправку нельзя: повтор после успешногоsend_messageдаст получателю дубль (см. скиллtelegram-send).
Жесткие правила
- Auth-файл и .session - НЕ кладем в проектную папку. Если проектная папка синхронизируется через облако (Yandex.Disk, Dropbox, iCloud), утечка api_hash и сессии в облако недопустима. Только в
~/.config/telegram-snapshot/(локально на каждом устройстве отдельно). - Первый интерактивный запуск делает пользователь сам в терминале. Bash из этого скилла на интерактивный ввод не способен. Подготовить auth.json, дать команду, дождаться отчета пользователя.
- Не запускать
iter_messagesнапрямую без get_dialogs() прогрева на свежей сессии - получишь PeerUser-ошибку. Прогрев один раз на стартеamain(). - При использовании публичных ключей Telegram Desktop - помнить, что это серая зона по ToS. Сами числа
api_id/api_hashне секрет (они в открытых исходниках tdesktop), но ответственность за использование лежит на пользователе. Ограничения: не использовать для коммерческой автоматизации; не выходить за объем активности живого пользователя (без массовых запросов, рассылок, бот-подобных паттернов); не подключать к публичным сервисам/ботам. Только некоммерческие персональные сценарии (свой архив чатов, дейлики). - Зеркало пишется вне синкаемой папки сразу, а не исключается после. История публичного канала или многолетней переписки это десятки мегабайт
result.json, и синхронизация непрерывна: между созданием файла и любым следующим действием он уже на всех устройствах, а исключение задним числом этого не отменяет. Отсюдаchats_rootвне проекта (шаг 6). Механика и порядок уборки уже уехавшего -rules/docs-maintenance.md, раздел "Технические артефакты в синкаемой папке".
Связанные файлы
scripts/telegram-snapshot.pyв корне проекта - эталонный скрипт инкрементального pull сiter_dialogs()прогревом и форматом записи, совместимым с Telegram Desktop export.scripts/telegram-deltas.pyв корне проекта - расчет дельт между текущим и предыдущим snapshot для блока "Новое в чатах" в дейликах.scripts/telegram-send.pyв корне проекта - отправка сообщения в чат от имени пользователя (переиспользует ту же авторизацию и.telegram-snapshot.json). Настройка отдельно не нужна - если snapshot работает, работает и отправка. См. скиллtelegram-send.scripts/telegram-pull-one.py- драйвер single-chat-by-id: pull одного чата по id в произвольную папку, минуя конфиг (см. одноименную секцию).~/.cache/telegram-snapshot/media/<chat_id>/- локальный несинкаемый кэш вложений (TTL 24ч, см. "Медиа-кэш вложений").~/.config/telegram-snapshot/auth.json- локальные credentials на устройство (не в проекте)..telegram-snapshot.jsonв корне проекта - проектная карта{label: chat_id}(или{label: {id, topic_id}}для фильтрации форум-чата по теме).
Модель и текущие назначения
Перед новым заданием и запуском применяй маршрутизацию моделей: свежий MODELS выше канонического дефолта. Границы данных и полномочий этого навыка сохраняются.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

