tg-agent (Telegram → SQLite → MCP)
Provides read-only access to Telegram groups, channels, and forum topics via your Telegram account (Telethon). Enables agents to list watched topics, fetch new messages, search full message history, get statistics, manage read markers, and extract entities, without any ability to send or modify Telegram content.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Telegram-группы и темы для агента в opencode
Read-only связка: Telethon (твой аккаунт) → локальный SQLite → MCP-сервер → агент.
Главная идея — агент видит не всё подряд, а только то, что ты отметил сам. Тем может быть десятки, в каналах — годы переписки; вываливать это в контекст бессмысленно. Поэтому:
ты выделяешь нужное (папка в Telegram или точечно темы через
watch.py),это синхронизируется в локальный индекс (инкрементально, старая история — чанками),
агент получает 16 read-only инструментов, которые знают твои алиасы и «что нового с прошлого раза».
Быстрый старт: одна команда
python3 start.pyОна сама решает, что нужно: нет ключей — спросит и запомнит; не залогинен — покажет QR; дальше открывает визуал в браузере, где всё остальное мышкой: 「+ из Telegram」 — список чатов прямо из Telegram (форум/группа/канал, пометка «в индексе»), фильтр, окно дней и кнопка «Слить»; ★ — отдать тему агенту. Ни флагов, ни имён чатов, ни запоминания команд.
Всё описанное ниже — те же шаги, но вручную, для тех, кому нужен точный контроль
(в терминале есть и запасной выбор чатов: python3 start.py --cli).
Related MCP server: telegram-user-mcp
Как «визуально выделить нужное»
Вариант 0 — рабочее пространство в браузере
python3 ui.py # http://127.0.0.1:8899Главный экран «Требует внимания» показывает просрочки, блокировки, открытые вопросы и непроверенные выводы. Разделы «Проекты», «Задачи», «Решения», «Вопросы» ведут к карточке с историей и сообщениями-доказательствами; «Загрузки» — раздел скачивания и пересылки медиа: задания, файлы, пересылки, подборки. Светлая тема по умолчанию, тёмная и системная доступны в настройках; мобильная компоновка — без постоянных трёх колонок переписки.
таблицы с фильтрами проекта, ответственного, статуса и срока, сортировкой и пагинацией;
единый поиск по сущностям и сообщениям (FTS5 с безопасным LIKE-фолбэком), фильтры периода/проекта/человека, история поиска и локальные сохранённые представления;
ручное исправление полей и проверка с аудитом; устаревшая версия карточки отклоняется с 409, новые события вновь требуют проверки;
изменения проекта используют event-id и отдельную явную отметку просмотра: она не меняет
watch.last_readи не подтверждает правильность извлечения;настройка привязки синхронизированных тем к проекту и ручной справочник людей;
раздел «Переписка»: дерево чатов/тем, watch, алиасы, закрепление, отметки прочтения, дата, FTS, загрузка старых сообщений и копирование выдержки для агента;
синхронизация и добавление чатов из Telegram запускаются только по нажатию, идущую синхронизацию можно отменить (
POST /api/sync/cancel);раздел 101 показывает только локальное состояние и запросы-кандидаты. Он не выполняет авторизацию, проверку сети или отправку и не двигает курсоры экспорта.
Ограничения: контракт полей задач 101 ещё не подтверждён. Наличие токена или
локального GUID не означает работающую интеграцию. Отклонение записи человеком
является локальной отметкой: экспортёр пока не использует её как запрет отправки.
Настройки уведомлений позволяют посмотреть сводку без отправки; реальные
получатели по-прежнему задаются в TG_NOTIFY_ROUTES на сервере.
Выбор пишется в ту же таблицу watch, которую читает MCP-сервер, — агент в
opencode видит отметки сразу, без перегенерации конфигов. watch.py остаётся
для терминала и скриптов.
Посмотреть без Telegram: python3 seed_demo.py demo.sqlite && TG_DB=demo.sqlite python3 ui.py.
Вариант A — папка в Telegram
В приложении: Settings → Folders → New folder, назови например AI Agent,
положи туда нужные каналы и группы. Отмечать в коде ничего не нужно — что в
папке, то и читает агент:
python3 sync.py --folder <id папки> # id видно в веб-версии Telegram в URL папки
sync.py --dry-run --folder 7 # сначала посмотреть, что попадётКаналы-вещания из папки берутся автоматически (раз положил — значит нужно). Пополнять папку можно и без приложения — агенту достаточно попросить:
python3 folders.py list # папки и их состав
python3 folders.py add --folder 15 --chat @ai_channel # доложить чат в папку
python3 folders.py remove --folder 15 --chat @ai_channelСоздавать и настраивать сами чаты можно из терминала (реальные операции —
только при TG_ALLOW_MANAGE=1):
python3 manage.py create --title "Моя группа"
python3 manage.py edit --chat -1002000001 --title "Новое"
python3 manage.py topic-create --chat @forum --title "Тема"Релиз-посты и readme тем
python3 announce.py draft --repo ../tg-agent --tag v0.10.0 --product tg-agent --tpl-out post.yaml
python3 announce.py post --tpl post.yaml --chat @whitebite_devsoft --topic 323 --send --react 🔥 --pin
python3 announce.py readme --topic-title "🪡 Stitch · ядро" --chat @whitebite_devsoft --topic 325 --sendПост собирается из YAML-шаблона: badge, title, what, inside (список),
ps, warning, links, banner. Баннер рендерится PIL в 1080x720, если в
шаблоне задан. Без --send любой режим — dry-run: печатает текст и сохраняет
баннер, к Telegram не обращается. Реальная отправка (кросспост --also,
реакция --react, пин --pin) только при TG_ALLOW_MANAGE=1. У MCP-сервера
есть зерцало для агента — инструмент tg_manage_post, регистрируется за тем же
гейтом и требует confirm=True.
Вариант B — точечно темы внутри одной группы
Когда группа одна (твоя AI), а тем в ней десятки:
python3 watch.py topics --chat @mygroup # темы с цифрами: сколько сообщений, закрыта ли
python3 watch.py add --chat @mygroup --topic 123456 --alias "ML-новости" --pin
python3 watch.py add --chat @mygroup --topic 789 --alias "Релизы"
python3 watch.py list # что отмечено и сколько там непрочитанного
python3 watch.py suggest # топ-тем по активности + готовые команды add
python3 sync.py --watched # синхронизировать только отмеченноеАлиас — это то, что увидит агент вместо chat_id=-1001234567890 / topic_id=789.
Оба варианта совмещаются: папка даёт каналы, watch.py — темы внутри них.
Инструменты агента (все read-only)
Инструмент | Что делает |
| Первым делом. Список отмеченного: алиас, название темы, сколько всего сообщений, сколько новых, дата последнего |
| Новые сообщения темы с момента прошлого прочтения. Основной для «что там нового» |
| История темы: |
| Полнотекстовый поиск по всей истории, включая посты двухлетней давности (SQLite FTS5, релевантность через bm25) |
| Активность: разбивка по темам, топ авторов, динамика по дням, медиа |
| Отметить тему прочитанной (агент зовёт сам только после того, как показал разбор) |
| Что лежит в индексе и за какой период |
| Что шерили: топ @упоминаний, t.me, инвайтов (с пометкой жива/истекла из кэша checklinks), URL и источников пересылок |
| Структура индекса: чаты, а по chat_id — его темы со счётчиками |
| Готовые выжимки новых сообщений (создаёт |
| Извлечённые сущности (задачи, решения, обещания, вопросы): project, kind, status, owner, due_ts; фильтры pack/kind/status/project/owner/chat_id и query — подстрока в title/body |
| Одна сущность по id: все поля и история событий — какое поле менялось, когда и с каким доказательством из сообщения |
| Открытые сущности со сроком: просроченные ( |
| Сущности с событиями новее |
| Сводка по проекту: counts по статусам, open, overdue, next_due, открытые вопросы, обещания без срока/ответственного, топ владельцев |
| Пробелы по проекту или пакету: открытые вопросы без ответа, обещания без срока и без ответственного, с причиной попадания |
Отправки нет вообще — пока не включён гейт: по умолчанию в server.py нет
инструмента, который что-то пишет в Telegram. Пишущих модуля четыре, все вне
MCP-сервера по умолчанию: notify.py (сообщения себе, за гейтом
TG_ALLOW_SEND=1), folders.py (состав папок — add/remove чатов;
контент не пишет), manage.py (создание и настройку чатов, участники,
инвайт-ссылки; за гейтом TG_ALLOW_MANAGE=1) и modules/downloads.py
(скачивание медиа на диск — гейт TG_ALLOW_DOWNLOADS=1; пересылка медиа в
явно указанный чат — гейт TG_ALLOW_FORWARDS=1).
MCP-инструменты управления чатами (tg_manage_*) регистрируются только при
TG_ALLOW_MANAGE=1 и требуют confirm=True для каждой операции;
tg_downloads_run/status/collections — при TG_ALLOW_DOWNLOADS=1,
tg_downloads_forward — при TG_ALLOW_FORWARDS=1 (статус и подборки
регистрируются при любом из двух гейтов), запуск задания и пересылка требуют
confirm=True. tg_sql (произвольный
read-only SQL) скрыт по умолчанию, включается TG_EXPOSE_SQL=1.
Диагностика
python3 doctor.py # офлайн: библиотеки, креды, сессия, БД, FTS5, self-test маскировки
python3 doctor.py --live # + реальное подключение к Telegram
python3 doctor.py --providers # opt-in: одна мини-компляция к настроенному extract/digest-провайдеру (живой ключ; мёртвый — HTTP 401)
python3 doctor.py --quality # заполненность owner/due/body по пакетам + гистограмма причин отказа из rejects_jsonГоняй перед первым sync.py и после любых «почему не работает».
Установка
python3 -m venv ~/.venvs/tg-agent
~/.venvs/tg-agent/bin/pip install -r requirements.txt # telethon, mcp>=2, cryptg, qrcodeapi_id / api_hash: https://my.telegram.org/apps (бесплатно).
Больше ничего руками делать не надо: login.py при первом запуске сам спросит
ключи и запомнит их в .env (файл в .gitignore). Хочешь заранее — скопируй
.env.example в .env и заполни: все скрипты (sync / login / live / watch /
server / ui / digest / doctor) читают .env сами, а переменные окружения,
если заданы, имеют приоритет. Кодировка файла не важна: UTF-8, UTF-8 с BOM
(notepad) и UTF-16 (Out-File в Windows PowerShell) читаются одинаково.
Первый логин: один скан QR
python3 login.pyВ терминале появится QR: Telegram → Настройки → Устройства → Подключить
устройство — наведи камеру. Телефон и код вводить не нужно (код из Telegram
приходит только тебе, обойти это нельзя — а QR убирает ввод целиком).
Если на аккаунте включена двухэтапная проверка — спросит облачный пароль
(до трёх попыток, ввод не отображается — следи за раскладкой); повторный запуск
не требует нового скана: QR уже принят, спросит только пароль.
Сессия ляжет в tg_agent_session.session и переиспользуется навсегда.
Альтернативы: login.py --phone (телефон+код),
login.py --export (напечатает TELEGRAM_SESSION_STRING для контейнеров).
Переменная TG_ENV_FILE управляет тем, что читается вместо .env (пустое
значение — не читать .env вовсе; так тесты и CI остаются герметичными).
Синхронизация
python3 sync.py --dry-run # что найдётся, ничего не записывая
python3 sync.py --folder 7 # всё из папки
python3 sync.py --watched # только watch-лист
python3 sync.py --chat @mygroup --days 30 # конкретная группа, окно 30 днейПервый запуск попросит QR уже не должен — логинься заранее login.py; но и sync.py
сам спросит телефон + код (или QR), если сессии нет. Сессия — в tg_agent_session.session:
это полный доступ к аккаунту, chmod 600, не коммитить.
Каналы с историей в годы
Обычная синхронизация идёт сверху вниз и обрывается на первом известном сообщении — до конца архива так не дойти. Для архива:
python3 sync.py --chat @old_channel --backfill 3000Бэкфилл идёт снизу вверх чанками по 3000, печатает прогресс
(чанк 0->42: просмотрено 2, новых 2) и сохраняет курсор после каждого чанка,
поэтому прерванный запуск продолжается с того же места. Для очень длинных
архивов крути в фоне и добавь задержку (по умолчанию 0.5 c между чанками) —
Telegram за агрессивный парсинг может ограничить аккаунт.
Повторный запуск любого варианта тянет только новое, а также подхватывает
правки сообщений (edit_date → edited_ts; в отчёте — edited_messages).
Для регулярности — cron раз в 15–60 минут: sync.py --watched.
Как определяются темы
Telegram хранит темы как треды: у сообщения внутри темы reply_to.forum_topic=True,
id темы — в reply_to.reply_to_msg_id. Сообщения без reply_to в форуме
относятся к General (id 1). Поэтому sync.py делает один проход по группе и
раскладывает сообщения по темам сам: иначе General недосчитывается.
Список тем — через messages.GetForumTopicsRequest(peer, offset_date, offset_id, offset_topic, limit). Обёртки в Telethon нет, а в functions.channels этого
метода нет вообще (там только ToggleForumRequest) — запрос делается напрямую.
Глобальный поиск: найти новые каналы по слову
python3 search.py "нейросети" # каналы, группы и юзеры
python3 search.py "нейросети" --type channel # только каналы-вещания
python3 search.py "ИИ" --type group --limit 30Обёртка над messages.SearchGlobalRequest — сервер Telegram сам ищет по
названиям и содержимому публичных чатов (обычный API-запрос, не парсинг).
Результат: тип, название, @username, подписчики; дальше sync.py --chat @username — и канал в индексе. Так закрывается сценарий «найди все каналы
по ИИ», с которого начинается кураторство: поиск → выбор → синк → watch.
Агент может искать сам: при TG_ALLOW_LIVE=1 у MCP-сервера появляется
инструмент tg_live_search (тоже только чтение) — агент находит каналы и
предлагает тебе их синхронизировать, сам ничего не добавляет.
Проверка инвайт-ссылок: жива или истекла
python3 checklinks.py --link "https://t.me/+AbCdEf" # конкретная ссылка
python3 checklinks.py # инвайты из watch-чатов
python3 checklinks.py --newest 50 # свежие из всего индекса
python3 checklinks.py --chat -100123 --days 7 # один чат, окно по времениОбёртка над messages.checkChatInvite — тем же запросом, каким клиент
Telegram показывает превью ссылки до кнопки «Присоединиться»: в чат не
вступает. Живая ссылка отдаёт название, участников и флаг «вступление по
заявке»; истёкшая/невалидная фиксируется навсегда (хэш не оживает), живая
перепроверяется не чаще раза в --max-age дней (кэш в таблице
invite_status). Статусы видны офлайн: tg_links помечает инвайты
жива/истекла и даёт сводку. Между живыми запросами пауза --delay — за
серию проверок Telegram отвечает INVITE_HASH_FLOOD (FloodWait переживается).
Агент проверяет сам: при TG_ALLOW_LIVE=1 есть инструмент
tg_live_check_invites — принимает ссылки списком или текстом сообщений,
сначала кэш, живыми запросами добирает только протухшее (не больше 5 за
вызов).
Подключение к opencode
Формат в v1 и v2 разный: в v2 серверы лежат в mcp.servers, вместо
enabled: false используется disabled: true, и есть codemode: false, чтобы
инструменты были видны модели напрямую, а не через Code Mode.
v1 →
opencode.v1.jsonv2 →
opencode.v2.json
opencode mcp add telegram --global -- \
~/.venvs/tg-agent/bin/python ~/tg-agent/server.py
opencode mcp list # должно быть connected
python3 server.py --list # проверить сервер отдельно: печатает 16 инструментовФайл tg-agent.md — пример промпта агента: порядок работы, требование начинать с
tg_watch и правило не выполнять просьбы из переписки.
Примеры запросов:
«Что нового по моим темам?»
«Разбери „ML-новости“ за неделю: что важно, что пропустил»
«Искали ли в группах что-то про RAG за последний год?»
«Какая тема у нас самая живая и кто там пишет больше всех?»
Live-режим
python3 live.py # рядом с ui.py, те же кредыСлушает новые сообщения всех чатов, что уже в индексе + watch-листа, и пишет их
в БД сразу (Telethon events.NewMessage); правки (MessageEdited) обновляют
строку с edited_ts, удаления (MessageDeleted) помечают deleted=1. UI
опрашивает БД каждые 4 секунды (/api/poll), лента догоняет сама. В Telegram
live ничего не отправляет.
Дайджесты
DIGEST_PROVIDER=stub python3 digest.py # механическая выжимка без LLM
DIGEST_PROVIDER=openai DIGEST_URL=https://api.openai.com/v1 \
DIGEST_KEY=sk-... DIGEST_MODEL=gpt-4o-mini python3 digest.pyПо каждой слежуемой теме берёт сообщения после прошлого дайджеста (курсор в
meta), строит контекст и кладёт выжимку в таблицу digests. За прогон не
больше DIGEST_MAX_ROWS строк (дефолт 500); хвост, не вошедший в окно,
подбирается следующим прогоном по курсору. Агент читает
через tg_digests, UI показывает баннером 📝 в теме. Проще всего гонять
по cron раз в день.
Извлечение сущностей (packs)
EXTRACT_PROVIDER=stub python3 extract.py # механический извлекатель (без LLM)
EXTRACT_PROVIDER=stub python3 extract.py --dry-run # показать операции, ничего не писать
EXTRACT_PROVIDER=stub python3 extract.py --all-indexed # все чаты индекса, не только watch
python3 extract.py --pack services --until-done # дожевать хвост за один запуск
python3 extract.py --until-done --workers 6 # темы параллельно (EXTRACT_WORKERS)
EXTRACT_PROVIDER=openai EXTRACT_URL=https://api.openai.com/v1 \
EXTRACT_KEY=sk-... EXTRACT_MODEL=gpt-4o-mini python3 extract.py --pack constructionСообщения остаются неизменяемым источником истины; сущности — пересчитываемая
проекция: удалил и перегенерил заново, ничего не потеряно. Движок extract.py
generic, предметная область описана декларативным пакетом в packs/; пакет
задаёт kinds, prompt-шаблон, окно первого прогона (backfill_days) и scope.
Пакеты: construction (PM для стройки: decision / task / promise / question)
и services (сервисы и триалы: service / trial / promo; ключ дедупа —
dedup_hint: домен или слаг сервиса, поэтому «Триал 30 дней на Acme» и
«Acme Cloud trial» схлопываются в одну сущность). Дедуп собирает один
сервис из разных чатов в одну сущность с несколькими источниками
(entity_sources); слияние пишется в entity_events (op='merge'), редирект
старого id — в entity_aliases, tg_entity резолвит его автоматически. Область
дедупа задаёт поле dedup_scope: у construction он по проекту, у services
остаётся глобальным. services прицелен в темы
watch-листа с проектом «сервисы» (watch.py add --project сервисы) — не
шумит в код-чатах; scope меняется в packs/services.py. В packs/construction.py
— scope_filter.require_match с exclude_project_regex: "^сервисы$":
констракшн требует непустой проект и не запускается на проекте
«сервисы», скоупы изолированы. --until-done
крутит раунды (по 200 сообщений на тему), пока хвост истории не кончится;
--workers N (или EXTRACT_WORKERS) дожёвывает темы параллельно — разовая
накачка базы на десятки тысяч сообщений вместо часов занимает минуты-десятки
минут, а каждый скоуп коммитится независимо. Один огромный скоуп делится на
окна msg_id и жуётся тем же пулом параллельно (EXTRACT_WINDOW_MIN, дефолт
2000 сообщений; --windows N): план и sub-курсоры окон живут в meta,
главный курсор продвигается одной транзакцией, когда все окна дожёваны, —
прерванный прогон продолжается с того же места.
По умолчанию область прогона — watch-лист; --all-indexed расширяет на все
пары (chat, topic) индекса — для массовой обработки «кучи групп», без
курирования. По каждой паре (пакет, тема) курсор extract:<chat>:<topic>:<pack>
в meta; за один прогон не больше 200 сообщений, остаток подберёт следующий.
Курсор после прогона всегда выставляется на максимальный msg_id обработанной
пачки (поле covered_upto_msg_id в операциях провайдера убрано; clamp по
overflow оставлен). Провайдер отвечает JSON-операциями new/update/close/reopen; каждая обязана
нести evidence — цитаты сообщений (minItems: 1). Цитата проверяется как подстрока текста
сохранённого сообщения (плюс транскрипты медиа); операция без валидного
evidence отклоняется, причины отказа пишутся в extraction_runs.rejects_json
(не больше 50 за прогон), нормализация цитаты сводит ё→е. Поле confidence
из схем пакетов убрано (колонка осталась, не заполняется). Операции применяются одной транзакцией
(entities, entity_events, extraction_runs), сбой провайдера курсор не
двигает. Срок из текста: если провайдер не дал due_ts, детерминированный
fallback в extract.py разбирает due_text — абсолютные даты (20.10.2026,
20.10.26), русские названия месяцев («19 августа»), «N числа», относительные
(«сегодня»/«завтра»/«послезавтра», «через N дней/недель/месяц») и ISO-даты —
всё резолвится по дате сообщения; срок ставится на конец дня (23:59:59 UTC),
а не на полночь. Update без явного due_ts больше не затирает ранее
выставленный точный срок.
supersedes: решение new/update может сослаться на открытую сущность того же
scope — цель закрывается, в entity_events пишется строка op='supersede'. Валидных операций больше 50 за прогон: применяются первые по позиции
evidence, остаток фиксируется как ops_overflow (status=partial), а курсор
остаётся до первой отброшенной — следующий прогон дочитает без потерь.
Батч, отклонённый контент-фильтром провайдера (data_inspection_failed),
бисектится до сообщения-триггера; триггер пропускается с фиксацией
run status='filtered', остальные сообщения обрабатываются штатно.
Сетевой сбой ретраит батч до 3 раз (интервал EXTRACT_NET_RETRY_DELAY,
по умолчанию 15с); сломанная область завершает прогон ненулевым кодом —
незавершённый хвост виден сразу, курсоры позволяют перезапустить с места.
Отредактированные и удалённые сообщения перепрогоняются: id пишутся в
meta-ключ dirty:{chat}:{topic}, следующий прогон темы их снимает и
переизвлекает по ним.
Теги проектов: python3 watch.py add ... --project "Объекд" или
python3 watch.py project --chat @mygroup --topic 789 --project "Объекд".
Проект у строки watch — сущности наследуют его от темы.
Автономный конвейер
python3 pipeline.py # sync -> media -> extract -> fuzzy -> export -> digest -> monitor
python3 pipeline.py --pack services # один пакет извлечения
python3 pipeline.py --skip sync # офлайн-шаги, без Telegram (--skip media/fuzzy/export тоже можно)
python3 pipeline.py --days-ahead 7 --send
python3 pipeline.py --loop 30 --send # демон: полный цикл каждые 30 минутОдна команда для автономного режима: шаги независимы, сбой одного не
останавливает остальные (упавший шаг — в отчёте и в exit code 1); каждый шаг
зажат по времени PIPELINE_STEP_BUDGET (дефолт 900с) — зависший шаг
помечается упавшим, конвейер не виснет; extract вне --loop дожёвывает хвост
до конца, в --loop — по окну за цикл (курсоры продолжают следующий).
run() захватывает локи proclock, поэтому разовый запуск не гоняется
параллельно с loop-демоном (лок занят — выход 0 «уже запущен»). В отчёте
report["extract"] — сводные ops_rejected/ops_overflow. После извлечения fuzzy сливает дубли
сущностей по нормализованному title (FUZZY_MAX_MERGES, дефолт 500), а
export дописывает JSONL-фид для внешних потребителей (по умолчанию
export.jsonl рядом с базой, путь — TG_EXPORT_PATH; формат строки
{"entity", "events", "exported_ts"}, курсор в integrations).
Регулярность — --loop N (демон, cron не нужен) или cron; live.py остаётся
отдельным демоном для мгновенной подхватки новых сообщений.
Автономка (Windows)
На машине настроены три компонента:
Компонент | Роль | Запуск |
| полный цикл каждые 30 минут (sync → media → extract → fuzzy → export → digest → monitor) | при входе в систему: |
| мгновенная подхватка новых сообщений в индекс | тот же |
задача | утренняя сводка в Saved Messages ( | Task Scheduler |
Каждый демон получает собственную копию Telethon-сессии
(tg_agent_session_cli, tg_agent_session_live, tg_agent_session_morning):
два клиента на одном session-файле конфликтуют через его SQLite
(database is locked). Копии делаются обычным копированием файла
авторизованной сессии — auth-ключ один, локальные базы разные.
Двойной старт блокирует proclock (байтовая блокировка <db>.pipeline.lock /
<db>.live.lock; при смерти процесса ОС снимает её сама — протухших файлов не
остаётся). Логи дописываются в pipeline.log / live.log / morning.log
(в gitignore). Утренней сводке sync не нужен — loop-демон и так держит данные
свежими, а Telegram-API не конкурирует. Задаче утренней сводки нужен
TG_MONITOR_FORCE=1: иначе дедуп monitor по хэшу отчёта глушит повторную
отправку в тот же день. Остановка: снять задачи
Get-CimInstance Win32_Process -Filter "Name='python.exe'" и убить PID'ы
демонов; для отключения автозапуска — удалить tg-agent.cmd из Startup и
schtasks /delete /tn tg-agent-morning /f.
Мониторинг и уведомления
python3 monitor.py # dry-run: сводка по проектам и дедлайны
python3 monitor.py --days-ahead 7 # добавить задачи на неделю вперёд
python3 monitor.py --project "Объекд" # один проект
python3 monitor.py --news-hours 24 # окно блока «новое» (по умолчанию сутки)
python3 monitor.py --send # отправить сводку в Saved MessagesСчитает по entities: просроченные — открытые сущности со сроком, чей due_ts
в прошлом; --days-ahead N дополнительно достаёт задачи до N дней включительно.
Сводка начинается с блока «новое за Nч» — свежесозданные сущности всех пакетов
и кросс-чатные повторы (события merge/dedup — сервис снова всплыл в другом
чате) со временем появления: утренний
pipeline.py --loop 1440 --send превращается в ежедневную сводку о триалах.
Есть блок «зависшие вопросы» — открытые question старше
MONITOR_QUESTION_DAYS (дефолт 3). Сканы ограничены по объёму.
--send дедупится по хэшу отчёта (meta['monitor:last_hash']): одинаковая
сводка второй раз не отправляется, TG_MONITOR_FORCE=1 обходит проверку.
--send — это notify.py:
python3 notify.py "текст" # dry-run: показать, что ушло бы
TG_ALLOW_SEND=1 python3 notify.py --send "текст"
echo "текст" | TG_ALLOW_SEND=1 python3 notify.py --send # из stdin, построчноnotify.py — модуль репозитория, который умеет писать сообщения в Telegram
(в свои Saved Messages). Реальная отправка только при TG_ALLOW_SEND=1, по
умолчанию везде dry-run; TG_NOTIFY_CAP (по умолчанию 10) ограничивает число
сообщений за вызов. Отправка идёт в worker-потоке на собственном event loop,
поэтому работает и изнутри уже запущенного цикла (pipeline --send раньше
падал с asyncio.run() cannot be called from a running event loop).
MCP-сервер notify.py не импортирует — агент отправки
по-прежнему не видит.
Медиа-транскрипция
MEDIA_PROVIDER=stub python3 media.py # заглушка без сети
MEDIA_PROVIDER=openai python3 media.py --limit 50 # скачивание + транскрипция
python3 media.py --sweep-pending # разовый дренаж легаси-pending -> skipped, без скачиванияsync.py и live.py ставят в очередь только транскрибируемое аудио —
голосовые и аудио-документы (гейт media.is_transcribable, обычные документы
в очередь больше не попадают); воркер рефетчит сообщение из Telegram по id
(файловая ссылка живёт только на свежем объекте), классифицирует аудио по
атрибутам (DocumentAttributeVoice/Audio, mime audio/* как фолбэк) и до
скачивания отсекает не-аудио, файлы крупнее 10MB и длиннее 5 минут статусом
skipped; удалённые сообщения — missing, сбой провайдера — failed.
openai-провайдер — DashScope qwen3-asr-flash через chat/completions: аудио
инлайнится base64 data-URL, asr_options идёт верхним уровнем
(MEDIA_URL/MEDIA_KEY/MEDIA_MODEL/MEDIA_TIMEOUT; ключ — список через
запятую с ротацией на 401/403). MEDIA_LIMIT (дефолт 25) ограничивает ассеты
за прогон, --limit/--kinds — то же в CLI. Скачивание файла реализовано
(Telethon download_media в _run_with_client); если байтов аудио нет,
openai-провайдер отвечает ValueError. Готовые транскрипции extract.py
подмешивает в промпт извлечения (и evidence-проверка учитывает их текст).
pipeline.py гоняет шаг автоматически после sync. --sweep-pending — разовый
дренаж строк pending, накопившихся до гейта is_transcribable: одним UPDATE
они помечаются skipped и не скачиваются.
Скачивание видео и медиа-библиотека
python3 downloads.py plan --sources watch --days 90 # задание без запуска
python3 downloads.py run --sources watch # создать и выполнить
python3 downloads.py run --job 3 --retry --force # продолжить/дожать
python3 downloads.py run --sources watch --mode forward --to saved
python3 downloads.py status [--job 3] # задания и библиотека
python3 downloads.py files [--chat -100123] # файлы с числом источников
python3 downloads.py forwards [--chat -100123] # пересланные медиа
python3 downloads.py collections save Видео --chats -100111,-100222
python3 downloads.py job pause|resume|cancel 3Скачивает видео (по желанию videonote и photo) из отмеченных каналов, папки,
подборки, всех чатов индекса или точечного чата. Источники — спека через
запятую: watch | all | folder:<id> | collection:<имя> | chat:<id|@username>.
Подборка (collections) — сохранённая группа каналов для повторяемых заданий;
watch-лист и доступ агента она не меняет.
Дедупликация точных дублей — два слоя. До скачивания — по id файла
Telegram: пересланный в другой чат ролик не качается повторно. После
скачивания — по sha256 содержимого: тот же ролик, залитый заново, остаётся
одной копией, а второй id файла алиасится на уже скачанный файл. Хранение
контентно-адресное: downloads/files/<sha[:2]>/<sha><ext> — одна строка
media_files на уникальное содержимое, у файла накапливаются все
сообщения-источники (media_file_sources). Повторное задание по умолчанию
не включает уже скачанные сообщения.
Задание — снимок списка сообщений на момент создания (таблицы jobs /
job_items), поэтому прерванный прогон продолжается с места остановки, а не
начинается заново; --retry дожимает упавшие пункты, pause/resume/
cancel управляют ходом. Один пункт не обрывает пакет: причины пропуска
(duplicate, duplicate-content, too-big, missing, kind,
chat-unavailable) и падения — в отчёте задания. Лимит размера —
TG_DOWNLOADS_MAX_MB (дефолт 2000, 0 — без лимита), каталог библиотеки —
TG_DOWNLOADS_DIR (дефолт downloads/ рядом с репо).
Режим forward — пересылка без скачивания. --mode forward --to saved
(или @username / числовой id своего чата) пересылает сообщения серверно:
байты медиа через клиент не идут, одна пересылка — один API-вызов. Дедуп
остаётся однослойным, по id файла Telegram и адресу (forward_files /
forward_sources): тот же файл из любого канала повторно не пересылается,
у записи накапливаются все сообщения-источники. Контентный sha256-дедуп здесь
невозможен — перезалитая копия с другим id пройдёт как новое видео. Ключ
реестра (doc_id, адресат): смена адресата — свои пересылки заново (в план
берутся через --all-messages). Длинный FloodWait (>120 с) уводит задание в
pause, короткий пережидается молча; каналы с запретом пересылки
пропускаются (forwards-restricted). Пересылка — операция записи в Telegram,
поэтому MCP-инструмент tg_downloads_forward живёт за отдельным гейтом
TG_ALLOW_FORWARDS=1 и требует confirm=True; HTTP-маршрут —
POST downloads/forward, список — GET downloads/forwards.
Раздел «Загрузки» в рабочем пространстве. ui.py показывает реестр
заданий с прогрессом и детали задания (параметры, счётчики, причины
пропусков, упавшие пункты) с кнопками «Запустить/Пауза/Продолжить/
Отменить/Дожать упавшие» и автообновлением, пока задание активно; форма
нового задания (источники-спека с быстрыми подстановками и подборками, режим
скачивание/пересылка, типы медиа, окно дней, лимит МБ) создаёт и запускает
прогон, списки файлов и пересылок фильтруются по чату, подборки создаются и
удаляются. UI использует те же маршруты downloads/*, что CLI и MCP.
Это первый модуль нового контракта: явный реестр modules/ (dict: id,
version, schema, workspace-маршруты, MCP-спеки, runner). Ядро подключает
модуль в трёх точках — db.py (миграции v7/v8 создают таблицы модуля),
workspace_api.py (маршруты downloads/* в UI API) и server.py
(MCP-инструменты за гейтом; у каждой спеки опциональный gate:
downloads, forwards, any). Общая инфраструктура — jobs.py
(сохраняемые задания) и sources.py (выбор источников + подборки): следующий
модуль переиспользует их без правки ядра. TG_MODULES_OFF="downloads"
убирает модуль целиком — ни маршрутов, ни инструментов, ни таблиц.
Похожие, но не идентичные ролики (пережатые, обрезанные, с вотермаркой) — кандидаты на слияние для человеческого решения: автоматически не удаляются и не сливаются, это следующий шаг модуля.
Экспорт
python3 export.py # JSONL рядом с базой
python3 export.py --system file --out /tmp/export.jsonl
python3 export.py --system 101 # POST в 101 (нужны EXPORT_101_URL/TOKEN)
python3 export.py --system notion # структурированная ошибка: нет API-доступаВыгружает сущности и их события во внешние системы. Путь file-провайдера:
--out, иначе TG_EXPORT_PATH, иначе export.jsonl рядом с базой. Курсор
каждого провайдера живёт в таблице integrations (entity_id, system):
выгружаются только сущности с событиями свежее курсора; сущности без
событий не выгружаются никогда. Курсор 101 — по entity_events.id, а не по
вторичному ts (события в одну секунду раньше молча терялись). 101 — реальный
REST-провайдер (stdlib urllib): EXPORT_101_URL (https://app.101-group.ru), auth —
EXPORT_101_TOKEN либо EXPORT_101_LOGIN + EXPORT_101_PASSWORD (вход
/api/auth/sign-in/, refresh пары на 401 через /api/auth/refresh-token/).
Сущности kinds из EXPORT_101_KINDS (дефолт task,promise) уходят задачами в
EXPORT_101_PATH (дефолт /api/tasks/v2/): create — POST, при наличии
integrations.external_id — PATCH .../{guid}/; EXPORT_101_COMMENTS=1 пишет
событие комментарием; переименование полей под контракт 101 — EXPORT_101_MAP
({"title","body","due_ts"} → поля 101); EXPORT_101_DRY=1 — показать, что уедет,
без отправки (курсор не двигается). 5xx/сеть ретраятся (EXPORT_101_RETRIES,
EXPORT_101_BACKOFF), 4xx не ретраится; упавшая сущность не обрывает прогон (уходит
в failed), external_id из ответа (guid) пишется в integrations.external_id,
курсор — только после 2xx; без кредов — структурированный {"ok": false, "error": ...}
без исключения. Тела задач в сваггере 101 нет — дефолтный EXPORT_101_MAP
консервативен и настраивается. notion остаётся заглушкой со словарём-ошибкой.
Безопасность
Отправить сообщение агент не может — по умолчанию таких инструментов нет. Пишущие модули живут вне MCP-сервера:
notify.py(сообщения в Saved Messages, гейтTG_ALLOW_SEND=1),folders.py(состав папок — только membership, без контента; правка = явный запуск команды),manage.py(создание и настройка чатов, участники, инвайт-ссылки; гейтTG_ALLOW_MANAGE=1) и downloads-модуль (скачивание медиа на диск — гейтTG_ALLOW_DOWNLOADS=1; пересылка медиа в явно указанный чат — гейтTG_ALLOW_FORWARDS=1). MCP-инструменты управления чатами (tg_manage_*) регистрируются только приTG_ALLOW_MANAGE=1и требуютconfirm=Trueдля каждой операции;tg_downloads_run/status/collections— приTG_ALLOW_DOWNLOADS=1,tg_downloads_forward— приTG_ALLOW_FORWARDS=1; запуск задания и пересылка требуютconfirm=True.Сообщения — источник истины, правки подхватываются. Строка отражает последнее состояние текста (
edited_ts); предыдущие версии правок не сохраняются. Удаление — флагdeleted=1: строка остаётся в базе, но читалки (история,tg_new, поиск,_around) её отдают с фильтромdeleted=0, аmark_deletedвычищает строку из FTS — удалённое не ищется, отредактированное ищется по новому тексту (триггер FTS —AFTER UPDATE OF text). Сущности — пересчитываемая проекция: каждая обязана цитатами из сохранённых сообщений (проверка на подстроку), без валидного evidence операция не применяется; цитата вentity_eventsможет ссылаться на текст до правки.Секреты маскируются на инджесте (
redact.py): API-ключи (openai/github/ groq/slack/nvidia), бот-токены, JWT, приватные ключи и «пароль: …»-паттерны заменяются на[скрыто: …]до записи в индекс — агент сырые креды не видит. Чтение (LIKE-фолбэк, UI,tg_sql, дайджесты) маскирует ещё раз — на случай баз, синхронизированных до появления маскировки.tg_sqlтолько чтение, по-настоящему: префикс SELECT/WITH, плюс отдельное соединение сPRAGMA query_onlyи authorizer, который разрешает только чтение —ATTACH,PRAGMA, записи и транзакции отклоняются, несколько запросов за раз SQLite не пропустит. Слова вродеupdateвнутри строковых литералов поисковых запросов больше не блокируются. По умолчанию инструмент выключен.Session-файл = аккаунт. 600, вне git. Если страшно — отдельный аккаунт для чтения.
Prompt injection. Чужой текст — данные, не инструкции; это сказано модели в
instructionsсервера и в промпте агента. Не включайTG_ALLOW_LIVEтам, где это критично.Индекс — твоя переписка в открытом SQLite. Держи локально.
Дайджесты от LLM — текст модели о чужих сообщениях; в инструменте
tg_digestsэто помечено как данные, не команды.TG_STRICT_WATCH=1— жёсткий гейт:tg_read/tg_find/tg_stats/tg_links/tg_topicsработают только по темам из watch-листа, на остальное агент получаетaccess_deniedс подсказкой попросить тебя отметить тему. Сущностные инструменты (tg_entities/tg_entity/tg_overdue/tg_changes/tg_gaps) гейтятся так же, но merged-сущность видна, если в watch-листе любой из её исходных чатов (entity_sources); её события могут цитировать другие участвовавшие в слиянии чаты.UI с наружу — только с токеном. При
HOST=0.0.0.0UI вообще не стартует безTG_UI_TOKEN. Локальный режим дополнительно закрыт от DNS-rebinding (проверка Host) и CSRF (POST принимается только сContent-Type: application/json), токен сверяется constant-time.FloodWait переживается автоматически: sync/backfill спят указанное Telegram время и повторяют (до 3 раз); SQLite в WAL с
busy_timeout, поэтомуlive.py,sync.pyи UI могут работать одновременно.
Структура репозитория
Файл | Роль |
| Telethon → SQLite: инкрементальная синхронизация, бэкфилл чанками, папки, пины, подхват правок ( |
| Глобальный поиск Telegram: публичные каналы/группы/юзеры по ключевому слову |
| Статус инвайт-ссылок (checkChatInvite без вступления): CLI + кэш |
| Правка состава папок Telegram: add/remove чатов (пишущий модуль, контент не пишет) |
| Создание и управление чатами: группы, каналы, форумы, настройки, участники (гейт |
| Релиз-посты: баннер PIL, шаблон YAML, readme тем из org.yaml, черновик из CHANGELOG; гейт |
| Реестр продуктов WhiteBite: репо, ссылки, эмодзи, разметка тем |
| Автономный конвейер: sync → media → extract → fuzzy → export → digest → monitor одним прогоном; бюджет шага, proclock-лок, ops-сводка в отчёте |
| Индекс: схема с миграциями по |
| MCP-сервер (stdio): 16 read-only инструментов; |
| Новые сообщения в реальном времени ( |
| Дайджесты: stub или openai-совместимый LLM |
| Извлечение сущностей по пакетам из |
| Декларативные пакеты извлечения: |
| Сводка просроченного и дедлайнов по проектам, зависшие вопросы; |
| Уведомления в Saved Messages; пишущий модуль, гейт |
| Очередь транскрипции аудио (voice + audio-документы): гейт |
| Реестр функциональных модулей (контракт: id, version, schema, workspace-маршруты, MCP-спеки, runner; |
| CLI модуля скачивания: план, прогон, статус, файлы, подборки, управление заданием |
| Сохраняемые задания: |
| Выбор источников: |
| Выгрузка сущностей во внешние системы (file / 101 — REST-задачи с JWT sign-in+refresh, id-курсор, ретраи, upsert по guid; notion — заглушка-ошибка) |
| Каталог сервисов из сущностей: rollup по dedup_key, |
| Maintenance-проход: слияние дублей сущностей по нормализованному title (richer-keeper, cap, идемпотентный) |
| Lockfile одиночного инстанса для демонов (pipeline --loop, live.py) |
| Рабочее пространство в браузере: «Требует внимания», сущности, «Переписка», «Загрузки»; раздаёт |
| Бэкенд рабочего пространства: фильтры/сортировка/пагинация, поиск FTS5+LIKE-фоллбек, правка полей с аудитом (409 на устаревшую ревизию), проверка карточек, курсор просмотра проекта, привязка тем, справочник людей, локальные превью 101 и уведомлений |
| Дизайн-спека рабочего пространства: палитра, типографика, сетка, светлая/тёмная темы |
| То же в терминале; |
| Логин QR-кодом (или телефон+код), |
| Чтение |
| Маскировка секретов / чистка bidi-инъекций (sanitize — из chigwell, Apache-2.0) |
| Диагностика окружения; |
| Демо-база без Telegram: |
| 1275 офлайн-проверок (без аккаунта Telegram) — свои проверки плюс пятнадцать suites ниже подпроцессами, гоняются в CI; своя temp-база через |
| правки/удаления сообщений в индекс, гейт медиа-очереди (запускается из |
| пак: срок из due_text, no-downgrade, изоляция по проекту, supersedes, схемы пакетов (запускается из |
| HTTP-экспорт 101 против локального фейк-сервера (запускается из |
| операционная автономка: notify в живом loop, бюджет шагов, lock, дедуп сводки, doctor --quality (запускается из |
| sweep pending + граница tg_mark_read (запускается из |
| user_version-миграции, read-only, deleted/FTS, dirty, каскад проекта (запускается из |
| читалки: изменения с прошлого раза, бриф проекта, пробелы, фильтр owner (запускается из |
| валидация схем паков при загрузке (запускается из |
| свежесть прогона конвейера ( |
|
|
| Офлайн-тесты manage.py: создание, настройки, темы, участники, MCP (запускается из |
| HTTP-тесты workspace-API на реальном сервере: фильтры, 409-ревизии, 401/415, отмена синхронизации, секреты не утекают, превью 101 без сети (запускается из |
| сигналы по вопросам: ответ/реакции не закрывают вопрос (запускается из |
| Офлайн-тесты announce.py: рендер баннера, сборка текста, post с отправкой на фейк-клиенте, readme из org.yaml, draft из CHANGELOG, гейт (запускается из |
| Офлайн-тесты downloads-модуля: дедуп doc-id/sha256 + алиасы, forward-режим (дедуп по файлу и адресату, FloodWait-пауза, запрет пересылки), resume, пауза/отмена, подборки, реестр и |
| Готовые конфиги подключения к opencode |
| Памятка-промпт для агента |
Лицензия — MIT (LICENSE); атрибуция заимствований — там же и в шапках файлов.
*.sqlite, *.session* и .env в .gitignore — в репозиторий не попадают.
Альтернативы: готовые MCP
Если не хочется своего кода:
Проект | Темы | Комментарий |
chigwell/telegram-mcp (~1.8k★) | ✅ | Самый полный: мультиаккаунт, Docker, |
❌ | 7 инструментов, | |
❌ | «суммаризируй чат», отправка только через |
⚠️ Не ставь pip install telegram-mcp / uvx telegram-mcp — в PyPI это
другой проект; передав ему API_HASH и session-string, отдашь доступ к
аккаунту постороннему коду. Только из git по явному URL.
Про клиентов
Под капотом всех MCP — обычные user-клиенты MTProto: Telethon (Python, здесь),
GramJS (JS/TS), TDLib (C++, удобнее для очень больших дампов),
opentele (конвертация tdata Telegram Desktop в Telethon-сессию). Разница
только в языке: данные MTProto отдаёт одни и те же.
Майнинг готовых MCP: что взято
Склонированы и прочитаны четыре репы; взято только то, что ложится в нашу архитектуру без чужих рантайм-зависимостей:
Откуда | Что | Статус |
chigwell/telegram-mcp |
| ✅ взято как есть, применяется ко всей выдаче сервера и UI |
chigwell/telegram-mcp | паттерн пинов ( | ✅ портирован в |
0xMRK95/telegram-mcp-server | regexp | ✅ портирован: таблица |
chigwell/telegram-mcp |
| ⛔ не берём: намертво привязан к их |
chigwell/telegram-mcp |
| 💡 идея учтена: у нас инструментов отправки нет вообще, |
m0n0x41d/telegram-mcp | scan-state с курсорами и TRUNCATED-семантикой | ≈ не берём: наши курсоры в |
m0n0x41d/telegram-mcp | auth-визард с 2FA и лечением протухшей сессии | кандидат на потом (у нас пока |
andvseti/telegram-mcp-server |
| кандидат для медиа-превью (слой 4) |
chigwell/telegram-mcp | Docker + HTTP-транспорт (общий демон на несколько клиентов) | кандидат, если визуал и opencode будут на разных машинах |
Проверено / не проверено
python3 test_offline.py — 1275 проверок: свои проверки плюс пятнадцать suites
подпроцессами (tests_index_media.py, tests_packs_semantics.py,
tests_export_101.py, tests_ops_report.py, tests_media_ingest.py,
tests_db_core.py, tests_readers.py, tests_pack_schema.py,
tests_watchdog.py, tests_ui_projects.py, tests_manage.py,
tests_announce.py, tests_workspace_api.py, tests_question_signals.py,
tests_downloads.py) — все зелёные (работают и на Linux, и на Windows). Реально исполняются:
sync.run()на подставном клиенте: определение групп/каналов,--pattern,--chat @канал,--folder,--watched,--dry-run, раскладка по темам (включая General), инкрементальность по курсорамsync.backfill(): досасывание истории чанками снизу вверх, идемпотентность, сохранение курсора, resume прерванного прогона (meta:backfill)id-формы Telethon: entity.id в marked-форме (-100…) на границе синка — live.py и watch.py видят одни и те же id
db.py: темы, история, окно по времени, FTS5-поиск (включая фильтр по чату), статистика, watch-лист (watch_new/watch_mark_read/ удаление), миграции поPRAGMA user_version(DDL не повторяется на каждом открытии), read-only-путь (DB(readonly=True)без миграций и сquery_only), каскад проекта вwatch_set_project, скрытиеdeleted=1от читалок;run_sqlи его гейты (query_only+ authorizer:DELETE,PRAGMA,SELECT; DROP,UPDATE,ATTACH— блокируются; слово в LIKE-литерале — нет)маскировка секретов на инджесте: токен не доезжает до
messages.text,raw_jsonиrefs; LIKE-фолбэк иrun_sqlвыдают замаскированноеFTS-триггёр удаления:
DELETE FROM messagesчистит индекс (раньше падал)правки/удаления в индексе:
db.update_message/db.mark_deleted, FTS отдаёт новый текст после правки, удалённое скрыто от читалок и из FTS; sync подхватываетedit_dateв rescan и backfill (edited_messagesв отчёте);live.handle_edit/live.handle_deleteна подставных событияхстатусная модель:
open / in_progress / blocked / done(терминал —closed_ts); opsstart/block/unblockс проверкой переходов,close→done; миграция v3 (closed→done);updateне трогает статуссигналы по вопросам: ответ другого отправителя (
reply_received) и реакции (reaction_received) требуют проверки, но сами не закрывают вопрос; завершение остаётся явной операцией извлечения или ручным исправлением статусареакции в индексе:
live.handle_reactions+db.update_reactions(агрегатные счётчики)читалки агента: изменения с прошлого раза, бриф проекта, пробелы (вопросы без ответа, обещания без срока/ответственного), фильтр по ответственному
MCP: 16 инструментов —
tg_changes/tg_project_brief/tg_gaps+ownerвtg_entitiesмаршрутизация сводок по проектам (
TG_NOTIFY_ROUTES, персональные хэш-ключи);monitorпоказывает обещания без срока/ответственного и «застряло» (blocked+просрочка)наблюдаемость:
doctor --cost, штампpipeline:last_ok_ts+watchdog.py, UI-панель/api/projectsс курсором визита проектагигиена: валидация схем паков на импорте, провайдеры экспорта из конфига (
TG_EXTRA_PROVIDERS)настоящий
server.pyкак подпроцесс по MCP stdio:initialize→tools/list(16 инструментов,tg_sqlскрыт) →tools/callпо каждому, включая сценарий «прочитал → отметил → непрочитанного нет»;tg_newчестно держитtruncatedпри лимите;tg_mark_readотклоняет id выше максимума индекса темы; отдельный прогон сTG_ALLOW_LIVE=1— live-инструменты регистрируются, без кредов отвечают JSON-ошибкой, а не зависаютdownloads-модуль: дедуп по id файла Telegram до скачивания и по sha256 после (повторная загрузка алиасится на файл), у файла все сообщения-источники, контентно-адресное хранение; forward-режим — пересылка без скачивания: дедуп по
(id файла, адресат), длинный FloodWait уводит задание вpause, короткий пережидается, запрет пересылки — skip; resume упавших пунктов (--retry --force), пауза/отмена задания, подборки каналов, реестр модулей иTG_MODULES_OFF, миграции v6→v8 и v7→v8 (таблицы пересылок довариваются в существующую базу); MCP-гейтыTG_ALLOW_DOWNLOADS=1(16+3) иTG_ALLOW_FORWARDS=1(16+3), оба — 16+4; запуск и пересылка —confirm=True; фоновый загрузчик без кредов помечает задание failed и не зависаетworkspace-API: реальный HTTP-сервер из
ui.py(ThreadingHTTPServer, токен): фильтры/сортировка/пагинация сущностей, правка полей с 409 на устаревшую ревизию, 401 без токена и 415 без JSON, отмена незапущенной синхронизации — 409, секреты маскируются, превью 101 не ходит в сеть и не двигает экспортные курсорыпапки:
GetDialogFiltersRequestна фейках → состав папок в индексетоп по реакциям (
stats.top_reactions)live.handle_message: вставка + идемпотентность + refs;collect_idsподхватывает чаты, добавленные после стартанастоящий
ui.pyкак подпроцесс:/api/pollотдаёт сообщения по курсору; Host-гейт (403 на чужой Host), POST безapplication/json→ 415, отказ поднимать0.0.0.0без токена;/api/messages,/api/poll,/api/stateвыдают замаскированный текстdigest.run: stub-выжимки создаются, повторно не дублируются (курсор), отдаются через MCPtg_digests; сбой LLM-провайдера не двигает курсоргейты:
FloodWaitErrorна фейках (sync дожидается и доделывает; посреди прохода счётчикnewне занижается),busy_timeout,before_id/date_to,budget_chars+truncated,tg_topics, live-тема с заголовком, UI-токен (401 без / 200 с)TG_STRICT_WATCH=1: второй сервер-подпроцесс — чужой чат отклонён, слежуемый читается,tg_find/tg_topicsвне watch слепыdoctor.run_checks(): формат чеков, FTS5 и redact self-test;--quality(заполненность полей по пакетам + гистограмма причин изrejects_json) и--providers(без настроенного openai-провайдера сеть не трогается)бюджет контекста дайджеста:
build_contextвписывается вDIGEST_MAX_CHARSи честно помечает обрезку; строки за прогон ограниченыDIGEST_MAX_ROWS(дефолт 500), хвост подбирается курсоромextract.run: курсор по (пакет, тема), окноbackfill_daysна первом прогоне, лимит 200 сообщений; операции new/update/close/reopen одной транзакцией; evidence-гейт: цитата вне текста сообщения отклоняется, причины отказа пишутся вrejects_json, сбой провайдера курсор не двигает; overflow: операции сверх 50 фиксируются вops_overflow, курсор остаётся до первой отброшенной, следующий прогон дочитывает;--all-indexedпокрывает все (chat, topic) индекса; дедупdedup_hintсильнее title; отдаётся через MCPtg_entities/tg_entity/tg_overduemonitor.collect: просрочка и горизонт--days-ahead, фильтр по проекту, блок зависших вопросов; дедуп--sendпо хэшу отчёта с обходом черезTG_MONITOR_FORCE=1;notify: dry-run по умолчанию, реальная отправка только заTG_ALLOW_SEND=1, capTG_NOTIFY_CAP;server.pyего не импортируетmedia: sync/live ставят в очередь только транскрибируемое аудио (voice + audio-документы, гейтis_transcribable), stub-транскрипт попадает в промпт извлечения; openai без скачанных байтов —ValueError;--sweep-pendingпомечает легаси-pending как skipped одним UPDATEexport.run: file-провайдер пишет JSONL, курсор вintegrations(повторный прогон дубликатов не даёт; краш посреди прогона — закоммичено до сущности, повтор выгружает только остаток); 101 — реальный HTTP-провайдер, проверен против локального фейк-сервера (POST, тело, id-курсор поentity_events.id, ретраи на 5xx, continue-on-error со спискомfailed,Idempotency-Key, записьexternal_id, курсор только после 2xx, ошибка без кредов); notion — словарь-ошибка без исключения. Живой пуш в 101 не проверялся (нет кредов/контракта)topic_id канонически NOT NULL (=1 General): вставка без темы и миграция старых баз с NULL, запросы идут по индексу
ix_msg_chat_topic(EXPLAIN PLAN проверяется), watch/history/search/stats видят то жеserver._db()кэширует соединение на поток — схема/миграции не гоняются на каждый tool-call; миграции дополнительно гейтятсяPRAGMA user_versiontg_live_*+tg_live_search: живые запросы регистрируются приTG_ALLOW_LIVE=1; без кредов — JSON-ошибка, не зависаниеsearch.search_global: каналы/группы/юзеры с marked-id и kind, фильтр типа уходит флагом в запрос (SearchGlobalRequest на фейк-клиенте)checklinks: хэш из +/joinchat/текста/сырого вида, checkChatInvite на фейк-клиенте (превью ok, member, expired/invalid терминальные, FloodWait повтором, сетевой сбой без падения прогона), кэш invite_status (свежее не перепроверяется, протухшее да, force не трогает терминальные, live_cap), invite_refs дедупит и сортирует по свежести, refs_summary джойнит статусы, миграция v5→v6;tg_live_check_invitesбез кредов — JSON-ошибкапакет
services: триал с due_text через dedup_hint-схему, другая формулировка с тем же hint — дубликат; stub создаёт service/trial/promoсемантика паков (
tests_packs_semantics.py): детерминированныйdue_text→due_ts(абсолютные даты, ru-месяцы, относительные, ISO — по дате сообщения, конец дня вместо полуночи), no-downgrade точного срока при update; изоляция construction от проекта «сервисы» черезrequire_match+exclude_project_regex;dedup_scopeproject/global; схемы пакетов: evidenceminItems=1, безconfidence; supersedes: цель закрывается, событие op='supersede' вentity_events. Качество и частота извлечения на реальных чатах не проверены: ratepromiseи реальная заполняемость due/supersedes ждут живого прогонаEXTRACT_PROVIDER=openaipipeline.run: sync→extract→digest→monitor на фейк-клиенте (10 сообщений, 2 дайджеста), второй прогон инкрементален,--skip sync— офлайн-шаги; в отчёте extract — сводныеops_rejected/ops_overflowоперационная автономка (
tests_ops_report.py): notify в живом event loop (worker-поток), бюджет шагаPIPELINE_STEP_BUDGET, proclock-лок one-shot, дедуп сводки monitor, зависшие вопросы,doctor --quality;tests_media_ingest.py: sweep pending и границаtg_mark_read;tests_db_core.py: user_version-миграции, read-only, deleted/FTS, dirty, каскад проектапроект у тем watch (
watch.py --project, подкомандаproject) наследуется сущностями и фильтруется вtg_entities/tg_overdue/monitormanage.py(tests_manage.py): создание групп/каналов/форумов на фейк-клиенте с регистрацией в индексе (created_by_me=1,my_role=owner), настройки (title/about/photo/username, тумблеры, дефолтные права), темы форума, участники и инвайт-ссылки; гейтTG_ALLOW_MANAGE(без него все операции заблокированы без запросов); MCPtg_manage_*регистрируются только при гейте и требуютconfirm=Trueannounce.py(tests_announce.py): рендер баннера PIL (1080x720), сборка текста из YAML-шаблона, post с кросспостом/реакцией/пином на фейк-клиенте и запись в таблицуposts(миграция v5), readme темы из org.yaml, черновик из CHANGELOG репозитория; гейтTG_ALLOW_MANAGE— без него отправка блокируется без запросов; MCPtg_manage_postтребуетconfirm=True. Живые посты в Telegram не проверены
Не проверено: реальные вызовы к Telegram — здесь нет аккаунта. Сигнатура
messages.GetForumTopicsRequest снята с установленного Telethon 1.45, но живой
ответ API глазами не видел; iter_dialogs(folder=...) проверен только по
сигнатуре. Первый sync.py --dry-run --folder <id> — это и есть проверка.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
This server cannot be deployed
Maintenance
Related MCP Connectors
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Read-only search over 2,065+ AI agent tools and 276 MCP servers. Free, no sign-up needed.
Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.
Your personal library, readable by AI agents: durable snapshots, hybrid search, 46 MCP tools.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA read-only MCP server that lets AI agents read personal Telegram chats from an allowlist of folders, with no send/edit/delete capability.41 npmMIT
- AlicenseNot gradedqualityCmaintenanceAn MCP server that connects to Telegram as your real user account and exposes read-only tools to read and search messages, list chats and folders, inspect group info, and download media.16 npmMIT
- AlicenseNot gradedqualityBmaintenanceRead-only MCP server for Telegram chats and channels that provides digest summaries, message search, and action items.11 npmMIT
- FlicenseNot gradedqualityBmaintenanceRead-only MCP server that exposes retrieval tools over a Telegram chat archive, including archive overview, full-text search with Russian morphology, aggregates, message retrieval, and local context. It lets LLM agents answer questions from chat history without syncing Telegram, downloading files, or modifying the index.-