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.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tg-agent (Telegram → SQLite → MCP)what's new in my watched Telegram topics?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Telegram-группы и темы для агента в opencode
Read-only связка: Telethon (твой аккаунт) → локальный SQLite → MCP-сервер → агент.
Главная идея — агент видит не всё подряд, а только то, что ты отметил сам. Тем может быть десятки, в каналах — годы переписки; вываливать это в контекст бессмысленно. Поэтому:
ты выделяешь нужное (папка в Telegram или точечно темы через
watch.py),это синхронизируется в локальный индекс (инкрементально, старая история — чанками),
агент получает 13 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Три колонки в тёмной теме Telegram Desktop: чаты → темы → сообщения. Рейка слева фильтрует: все / ★ мои / форумы / каналы. Что умеет:
звёздочка на теме — отметить для агента (или «★ отметить все темы» на чат);
алиас на отмеченную тему — имя, которое агент увидит в
tg_watch;синие бейджи — непрочитанное с момента «✓ прочитано»;
поиск по чатам и FTS-поиск по всей истории прямо в панели сообщений;
📁 рейка показывает твои папки Telegram — фильтр по составу папки;
📌 баннер закреплённых сообщений темы («readme») над лентой;
📝 баннер дайджеста, если он есть для темы (см. «Дайджесты»);
поле «к дате» — прыжок к истории за любое число (полезно для летних архивов);
выделение сообщений → «копировать для агента» — вставляет в opencode разом;
live: при запущенном
live.pyлента сама догоняет новые сообщения;кнопка ⟳ —
sync.py --watchedв фоне (нужны креды; статус виден под поиском).
Выбор пишется в ту же таблицу 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Вариант 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, инвайтов, URL и источников пересылок |
| Структура индекса: чаты, а по chat_id — его темы со счётчиками |
| Готовые выжимки новых сообщений (создаёт |
| Извлечённые сущности (задачи, решения, обещания, вопросы): project, kind, status, owner, due_ts; фильтры pack/kind/status/project/chat_id и query — подстрока в title/body |
| Одна сущность по id: все поля и история событий — какое поле менялось, когда и с каким доказательством из сообщения |
| Открытые сущности со сроком: просроченные ( |
Отправки нет вообще: в server.py не существует инструмента, который что-то
пишет в Telegram. Пишущих модуля два, оба вне MCP-сервера: notify.py
(сообщения себе, за гейтом TG_ALLOW_SEND=1) и folders.py (состав папок —
add/remove чатов; контент не пишет). tg_sql (произвольный
read-only SQL) скрыт по умолчанию, включается TG_EXPOSE_SQL=1.
Диагностика
python3 doctor.py # офлайн: библиотеки, креды, сессия, БД, FTS5, self-test маскировки
python3 doctor.py --live # + реальное подключение к TelegramГоняй перед первым 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 за агрессивный парсинг может ограничить аккаунт.
Повторный запуск любого варианта тянет только новое. Для регулярности — 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 (тоже только чтение) — агент находит каналы и
предлагает тебе их синхронизировать, сам ничего не добавляет.
Подключение к 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 # проверить сервер отдельно: печатает 13 инструментовФайл tg-agent.md — пример промпта агента: порядок работы, требование начинать с
tg_watch и правило не выполнять просьбы из переписки.
Примеры запросов:
«Что нового по моим темам?»
«Разбери „ML-новости“ за неделю: что важно, что пропустил»
«Искали ли в группах что-то про RAG за последний год?»
«Какая тема у нас самая живая и кто там пишет больше всех?»
Live-режим
python3 live.py # рядом с ui.py, те же кредыСлушает новые сообщения всех чатов, что уже в индексе + watch-листа, и пишет их
в БД сразу (Telethon events.NewMessage). 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. Агент читает
через 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 резолвит его автоматически. services прицелен в темы
watch-листа с проектом «сервисы» (watch.py add --project сервисы) — не
шумит в код-чатах; scope меняется в packs/services.py. --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 сообщений, остаток подберёт следующий.
Провайдер отвечает JSON-операциями new/update/close/reopen; каждая обязана
нести evidence — цитаты сообщений. Цитата проверяется как подстрока текста
сохранённого сообщения (плюс транскрипты медиа); операция без валидного
evidence отклоняется. Операции применяются одной транзакцией
(entities, entity_events, extraction_runs), сбой провайдера курсор не
двигает. Валидных операций больше 50 за прогон: применяются первые по позиции
evidence, остаток фиксируется как ops_overflow (status=partial), а курсор
остаётся до первой отброшенной — следующий прогон дочитает без потерь.
Батч, отклонённый контент-фильтром провайдера (data_inspection_failed),
бисектится до сообщения-триггера; триггер пропускается с фиксацией
run status='filtered', остальные сообщения обрабатываются штатно.
Сетевой сбой ретраит батч до 3 раз (интервал EXTRACT_NET_RETRY_DELAY,
по умолчанию 15с); сломанная область завершает прогон ненулевым кодом —
незавершённый хвост виден сразу, курсоры позволяют перезапустить с места.
Теги проектов: 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); extract
дожёвывает хвост сообщений до конца. После извлечения 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 не конкурирует. Остановка: снять задачи
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 превращается в ежедневную сводку о триалах.
--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) ограничивает число
сообщений за вызов. MCP-сервер notify.py не импортирует — агент отправки
по-прежнему не видит.
Медиа-транскрипция
MEDIA_PROVIDER=stub python3 media.py # заглушка без сети
MEDIA_PROVIDER=openai python3 media.py --limit 50 # скачивание + транскрипцияsync.py и live.py ставят голосовые и документы в очередь (таблица
media_assets, status=pending); воркер рефетчит сообщение из 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. Готовые транскрипции extract.py
подмешивает в промпт извлечения (и evidence-проверка учитывает их текст).
pipeline.py гоняет шаг автоматически после sync.
Экспорт
python3 export.py # JSONL рядом с базой
python3 export.py --system file --out /tmp/export.jsonl
python3 export.py --system notion # структурированная ошибка: нет API-доступаВыгружает сущности и их события во внешние системы. Путь file-провайдера:
--out, иначе TG_EXPORT_PATH, иначе export.jsonl рядом с базой. Курсор
каждого провайдера живёт в таблице integrations (entity_id, system):
выгружаются только сущности с событиями свежее synced_ts; сущности без
событий не выгружаются никогда. 101 и notion не реализованы — отвечают
словарём-ошибкой, исключений не бросают (у 101 есть публичный REST API: JWT,
swagger на app.101-group.ru — ждёт кредов).
Безопасность
Отправить сообщение агент не может — таких инструментов нет. Пишущие модули живут вне MCP-сервера:
notify.py(сообщения в Saved Messages, гейтTG_ALLOW_SEND=1) иfolders.py(состав папок — только membership, без контента; правка = явный запуск команды).Сообщения — неизменяемый источник истины. Извлечение сущностей — пересчитываемая проекция: каждая сущность обязана цитатами из сохранённых сообщений (проверка на подстроку), без валидного evidence операция не применяется.
Секреты маскируются на инджесте (
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) гейтятся так же, но 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: публичные каналы/группы/юзеры по ключевому слову |
| Правка состава папок Telegram: add/remove чатов (второй пишущий модуль, контент не пишет) |
| Автономный конвейер: sync → media → extract → fuzzy → export → digest → monitor одним прогоном |
| Индекс: схема, FTS5, watch-лист, refs, пины, дайджесты, |
| MCP-сервер (stdio): 13 read-only инструментов |
| Новые сообщения в реальном времени ( |
| Дайджесты: stub или openai-совместимый LLM |
| Извлечение сущностей по пакетам из |
| Декларативные пакеты извлечения: |
| Сводка просроченного и дедлайнов по проектам; |
| Уведомления в Saved Messages; единственный пишущий модуль, гейт |
| Очередь транскрипции голосовых и документов: рефетч из Telegram, гейты до скачивания, qwen3-asr-flash (stub/openai) |
| Выгрузка сущностей во внешние системы (file / 101 / notion) |
| Каталог сервисов из сущностей: rollup по dedup_key, |
| Maintenance-проход: слияние дублей сущностей по нормализованному title (richer-keeper, cap, идемпотентный) |
| Lockfile одиночного инстанса для демонов (pipeline --loop, live.py) |
| Веб-визуал «как в TG Desktop» (кураторство watch-листа) |
| То же в терминале; |
| Логин QR-кодом (или телефон+код), |
| Чтение |
| Маскировка секретов / чистка bidi-инъекций (sanitize — из chigwell, Apache-2.0) |
| Диагностика окружения |
| Демо-база без Telegram: |
| 475 офлайн-проверок (без аккаунта Telegram), гоняются в CI |
| Готовые конфиги подключения к 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 — 475 проверок, все зелёные (работают и на 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/ удаление),run_sqlи его гейты (query_only+ authorizer:DELETE,PRAGMA,SELECT; DROP,UPDATE,ATTACH— блокируются; слово в LIKE-литерале — нет)маскировка секретов на инджесте: токен не доезжает до
messages.text,raw_jsonиrefs; LIKE-фолбэк иrun_sqlвыдают замаскированноеFTS-триггёр удаления:
DELETE FROM messagesчистит индекс (раньше падал)настоящий
server.pyкак подпроцесс по MCP stdio:initialize→tools/list(13 инструментов,tg_sqlскрыт) →tools/callпо каждому, включая сценарий «прочитал → отметил → непрочитанного нет»;tg_newчестно держитtruncatedпри лимите; отдельный прогон сTG_ALLOW_LIVE=1— live-инструменты регистрируются, без кредов отвечают JSON-ошибкой, а не зависаютпапки:
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бюджет контекста дайджеста:
build_contextвписывается вDIGEST_MAX_CHARSи честно помечает обрезкуextract.run: курсор по (пакет, тема), окноbackfill_daysна первом прогоне, лимит 200 сообщений; операции new/update/close/reopen одной транзакцией; evidence-гейт: цитата вне текста сообщения отклоняется, сбой провайдера курсор не двигает; overflow: операции сверх 50 фиксируются вops_overflow, курсор остаётся до первой отброшенной, следующий прогон дочитывает;--all-indexedпокрывает все (chat, topic) индекса; дедупdedup_hintсильнее title; отдаётся через MCPtg_entities/tg_entity/tg_overduemonitor.collect: просрочка и горизонт--days-ahead, фильтр по проекту;notify: dry-run по умолчанию, реальная отправка только заTG_ALLOW_SEND=1, capTG_NOTIFY_CAP;server.pyего не импортируетmedia: sync/live ставят voice/document в очередь, stub-транскрипт попадает в промпт извлечения; openai без байтов —NotImplementedErrorexport.run: file-провайдер пишет JSONL, курсор вintegrations(повторный прогон дубликатов не даёт; краш посреди прогона — закоммичено до сущности, повтор выгружает только остаток); 101/notion — словарь-ошибка без исключенияtopic_id канонически NOT NULL (=1 General): вставка без темы и миграция старых баз с NULL, запросы идут по индексу
ix_msg_chat_topic(EXPLAIN PLAN проверяется), watch/history/search/stats видят то жеserver._db()кэширует соединение на поток — схема/миграции не гоняются на каждый tool-calltg_live_*+tg_live_search: живые запросы регистрируются приTG_ALLOW_LIVE=1; без кредов — JSON-ошибка, не зависаниеsearch.search_global: каналы/группы/юзеры с marked-id и kind, фильтр типа уходит флагом в запрос (SearchGlobalRequest на фейк-клиенте)пакет
services: триал с due_text через dedup_hint-схему, другая формулировка с тем же hint — дубликат; stub создаёт service/trial/promopipeline.run: sync→extract→digest→monitor на фейк-клиенте (10 сообщений, 2 дайджеста), второй прогон инкрементален,--skip sync— офлайн-шагипроект у тем watch (
watch.py --project, подкомандаproject) наследуется сущностями и фильтруется вtg_entities/tg_overdue/monitor
Не проверено: реальные вызовы к Telegram — здесь нет аккаунта. Сигнатура
messages.GetForumTopicsRequest снята с установленного Telethon 1.45, но живой
ответ API глазами не видел; iter_dialogs(folder=...) проверен только по
сигнатуре. Первый sync.py --dry-run --folder <id> — это и есть проверка.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read-only MCP server: let AI agents read your ORANO saved-video library, tasks, and memory.
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
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.28 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.13 npmMIT
- AlicenseNot gradedqualityBmaintenanceA read-only Telegram MCP server that only exposes a user-defined allowlist of chats, providing tools to list chats, fetch messages, search, and get context while stripping untrusted text and with no write capabilities.MIT