Skip to content
pockerheadPublic

About

Bridge between local Claude Code sessions and one private Telegram forum (Rust)

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

cctg: Claude Code в Telegram

ci release image

Сессии Claude Code на ваших машинах видны в одной закрытой Telegram-группе с темами. Каждая папка проекта получает свою тему: там видно, что делает Claude, приходят его ответы и кнопки разрешений, а ваше сообщение в теме уходит в сессию.

ноутбук, ПК, сервер          сервер с Docker             Telegram
claude-cctg в папке  ── TLS ──>  cctg hub  ── Bot API ──>  группа: тема на папку

Hub это один процесс с ботом. Он живёт на сервере (или на одной из ваших машин). На каждой машине с Claude Code стоит клиент cctg, его ставит одна команда.

Что нужно

  • Telegram-бот и группа с темами (ниже, 5 минут).
  • Сервер с Docker для hub. Можно и без сервера: hub на своей машине, docs/poc.md.
  • На каждой машине с сессиями: Linux, macOS (Apple Silicon) или Windows. Claude Code установщик поставит сам, если его нет.
  • Windows: Git for Windows (в нём Git Bash, через него работают установщик и хуки cctg). Если его нет: winget install Git.Git, потом откройте Git Bash.

1. Бот и группа

  1. В Telegram у @BotFather: /newbot, получить токен бота.
  2. Создать группу, в настройках включить «Темы» (Topics).
  3. Добавить бота в группу и сделать админом с правами «Управление темами», «Удаление сообщений», «Закрепление сообщений».
  4. Написать в группу любое сообщение и открыть https://api.telegram.org/bot<токен>/getUpdates: chat.id (начинается с -100) это id группы, from.id это ваш id.
  5. Необязательно, личка с темами: в @BotFather откройте Mini App, My bots → бот → Bot Settings → Threads Settings, включите Threaded Mode, а создание тем пользователями там же лучше выключить. Потом откройте личный чат с ботом и нажмите Start. Hub при старте видит режим сам (getMe): новые сессии получают тему только в вашей личке, а сессии, у которых уже есть тема в группе, идут в обе.
  6. Необязательно, ещё группы (с личкой из шага 5): добавьте бота в другую группу с темами администратором с правами «Управление темами» и «Удаление сообщений» от своего имени, не анонимно (иначе Telegram не скажет боту, кто его добавил, и бот из группы выйдет). Hub подключает группу сам, без перезапуска и без правки .env, и пишет в её General, что она подключена или чего не хватает. Если бот уже был в группе или права поменяли позже, напишите там /connect@<имя_бота> (без админских прав бот видит только команды со своим именем). Группа из CCTG_CHAT_ID остаётся основной.

2. Hub на сервере

На сервере с Docker:

curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh -s -- --hub

Установщик спросит токен бота (не показывается при вводе), id группы, ваш id, адрес сервера и, если Telegram с сервера доступен только через прокси, адрес прокси. Он сам сделает секрет и сертификат, запустит hub в Docker и проверит бота и группу. В конце он печатает строку для установки клиента. В ней одноразовый код: он работает один раз и 10 минут, секрета hub в строке нет. Код для следующей машины: cd ~/cctg-hub && docker compose exec hub cctg hub code.

Подробности (прокси, обновление, порты): docs/remote-hub.md.

Или hub на этой машине, без Docker

curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh -s -- --hub --local

Те же вопросы; прокси остаётся только в hub.env. Hub запускается при входе в систему без окон: в Windows запись автозагрузки (HKCU\...\Run, Windows Script Host запускает cctg supervise скрыто), в Linux пользовательский юнит systemd cctg-hub.service, в macOS LaunchAgent. Всё лежит в ~/.cctg/hub (hub.env, состояние, лог hub.log). Без --public-host ХОСТ hub пускает только эту машину; с ним слушает все адреса по TLS, и строку для других машин даёт /join. В конце установщик печатает строку клиента для этой машины. Linux: hub живёт, пока вы в системе; чтобы работал и без входа, loginctl enable-linger.

3. Клиент на каждой машине

Выполните строку, которую напечатал hub (в Windows это Git Bash). Она выглядит так:

curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh -s -- --hub-host <сервер> --pin <отпечаток> --join XXXX-XXXX-XXXX-XXXX

Установщик обменяет код на собственный секрет этой машины и запишет его в ~/.cctg/device.env, на экран секрет не попадает. Для каждой машины нужен свой код: /join в теме General отвечает готовой строкой с новым кодом (только людям из allowlist); сообщение потом говорит, что код использован (и каким устройством) или истёк. Список машин и кнопка «Отозвать»: /devices в теме General.

Без строки можно так, установщик всё спросит:

curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh

Установщик ставит cctg в ~/.cctg, команду claude-cctg в ~/.local/bin и проверяет связь с hub. Ваши ~/.claude/settings.json и ~/.claude.json он не трогает. Если ~/.local/bin нет в PATH, он предлагает дописать его одной строкой в ~/.zshrc, ~/.bashrc или ~/.profile (по вашему shell; с --yes без вопроса, --uninstall строку убирает). В Docker-контейнере имя машины это id контейнера, поэтому установщик спрашивает имя для тем (или --host NAME).

Проверить связь потом: ~/.cctg/bin/cctg doctor.

4. Запуск

В папке проекта вместо claude:

claude-cctg

Все аргументы claude работают так же (claude-cctg --resume, claude-cctg "почини тест"). Claude Code при старте спрашивает про development channels: выберите пункт 1. В Windows в обычной консоли (PowerShell, cmd, Windows Terminal), на Linux и macOS в терминале cctg нажимает за вас; в окне Git Bash (mintty) нет, поэтому сессии в Windows лучше запускать из PowerShell или cmd.

Сэндбокс папки (Linux, macOS)

~/.cctg/bin/cctg sandbox on в папке проекта: сессии claude-cctg в ней и во вложенных папках не читают и не пишут ничего вне папки (команды, скрипты, которые пишет модель, файловые инструменты, send_file, ! и опасные слеш-команды из Telegram). Выключить: cctg sandbox off, состояние: cctg sandbox status. На Windows нужен один раз cctg sandbox-install (один UAC создаёт скрытые учётки сэндбокса). Что закрыто, что нет и что для этого нужно на машине: docs/sandbox.md.

Что видно в Telegram

  • Тема [машина] папка · название сессии. Иконка показывает, жива сессия, ждёт ли разрешения.
  • Ваши промпты, текст Claude, по строке на каждый вызов инструмента и финальный ответ хода.
  • Запрос разрешения приходит с кнопками Allow и Deny.
  • Закреплённое сообщение со статусом: модель, контекст, лимиты.
  • Сообщение в теме уходит в сессию. Файлы и фото тоже.
  • Голосовое hub в Docker распознаёт офлайн: в теме ответ «Голосовое сообщение распознано. Текст: …», сессия получает текст и файл (docs/remote-hub.md, «Голосовые»).
  • В общей теме группы голосовое обращается к агенту подписью с @имя_бота или ответом на сообщение бота (не на его пост с текстом голосового); «бот, …», сказанное голосом, обращением не считается.
  • /brief и /full в теме присылают транскрипт сессии.
  • Сессия закрыта: сообщения ждут в теме (до 50) до следующего запуска в этой папке.
  • Команда в одной группе: в CCTG_ALLOWED_USER_IDS несколько id через запятую, все равны. Тогда Claude видит, кто пишет (Имя: текст, username или имя из Telegram), а ответ на запрос разрешения подписан именем нажавшего.
  • Люди из меню (Threaded Mode бота: меню живёт в личке): id из CCTG_ALLOWED_USER_IDS это владельцы. Остальных владелец добавляет и удаляет во вкладке меню «👥 Люди», без рестарта hub и без правки .env: переслать в General лички с ботом любое сообщение человека (бот спросит подтверждение кнопками) или, если Telegram скрывает его аккаунт в пересылках, дать одноразовую ссылку-приглашение (10 минут): человек открывает её и нажимает Start, владелец подтверждает. Участник пишет сессиям и отвечает на вопросы и разрешения, как владелец, но не подключает и не отзывает устройства (/join, /devices) и не меняет список. Удаление (🗑, второе нажатие подтверждает) сразу закрывает доступ, удаляет его меню, отключает его устройства и сообщает ему об этом, но не убирает его из групп Telegram. Владельцев из меню удалить нельзя. Список хранится в registry.json hub.
  • Личка с темами (Threaded Mode бота, шаг 5 выше): новая сессия получает тему только в личке владельца устройства; слот, у которого уже была тема в группе, остаётся и там, и группа повторяет личку. Показать сессию группе: /share в её теме в личке или кнопка «👥 В группу» на статусе там; в группе появляется тема, первая строка в ней ── общий доступ: <имя> ── (имя и фамилия из Telegram того, кто нажал; username, только если имени нет); сообщения до share в тему не переносятся, но /brief и /full в этой теме показывают всю сессию, включая ходы до share. Убрать: /unshare или «🙈 Убрать из группы» (кнопка просит второго нажатия); бот удаляет тему группы со всеми сообщениями, а без права удалять сообщения тема остаётся с пометкой и иконкой завершённой сессии и больше не обновляется. Делать это может только владелец, из своей лички: /share в группе получает отказ. Общий доступ принадлежит слоту и сохраняется для следующих сессий в нём. Если личка недоступна (нет Start, бот заблокирован), новая сессия идёт в группу, как раньше. Владелец: первый id в CCTG_ALLOWED_USER_IDS; в команде тот, кто сделал /join этого устройства, а устройства на общем секрете и вступившие до этой версии принадлежат первому id. Писать и нажимать кнопки можно в любом из двух мест: сессия получает сообщение один раз, в другом месте оно появляется с подписью автора (✉ имя: текст); первое нажатие решает, оба сообщения правятся. У лички и группы свои лимиты: 429 в одном месте не тормозит другое. Если группа отстаёт (у неё около 20 запросов в минуту на всё), часть сообщений в ней пропускается, в личке есть всё. Без Start в личке бот пишет об этом в General группы, сессии идут только в группу, пока вы не нажмёте Start; после Start (или разблокировки) слот, попавший в группу только поэтому, снова живёт в одной личке, а его тема в группе получает прощальное сообщение и больше не обновляется. Запрос разрешения или вопрос, ждавший ответа в этой теме группы, приходит заново в личку (копия в группе теряет кнопки; запрос, на который уже ответили в терминале, не повторяется), а на сообщение, написанное в старую тему группы, бот отвечает тем же прощальным текстом (до перезапуска hub, потом такие сообщения молча не доходят). Откат на v0.1.15: docs/remote-hub.md, раздел «Обновление».
  • Общая тема в группе отвечает только на обращения: @имя_бота в сообщении или подписи (регистр не важен) или ответ на сообщение бота. Остальные сообщения группы в сессию сразу не уходят и в личке не повторяются; группа один раз получает подсказку, как обратиться. С обращением сессия получает историю темы с прошлого обращения с авторами, затем само обращение. История длиннее лимита (2000, 4000 или 8000 символов, «👁 Показ» → «👥 Группа», по умолчанию 4000) сжимается на устройстве сессии запуском claude -p --model haiku без инструментов, хуков и MCP (он тратит лимит вашего аккаунта Claude и не создаёт темы); если сжать не вышло за минуту, остаются самые новые сообщения. Файлы в истории видны как [фото], [файл имя] и не скачиваются; альбом, в подписи которого есть обращение, уходит в сессию целиком (файлы альбома, попавшие в историю чужого обращения, второй раз не идут; после перезапуска hub посреди альбома его оставшиеся файлы остаются в истории). Если файл обращения не скачался, история и подпись всё равно доходят текстом, и бот просит прислать ещё раз только файл. Пока идёт сжатие, следующие сообщения этой сессии ждут. Кнопка 📣 в строке сессии меню переключает тему на «все сообщения», 💬 возвращает режим обращений. Сообщения из лички идут как раньше.
  • Меню в личке (Threaded Mode): с первой темой сессии в личке бот присылает в General лички меню и закрепляет его; /start или /menu там присылают его заново (старое и сама команда удаляются). Три вкладки. «Сессии»: живые и до 12 завершённых сессий ваших устройств с состоянием, кнопки ↗ (тема поднимается наверх списка), 👥/🙈 (в группу и из неё, как на статусе), ⏹ (прервать ход, второе нажатие подтверждает), 🔒/🔓 (сэндбокс папки сессии на её устройстве, только у владельца; 🔓 подтверждается вторым нажатием, сессия перезапускается сама, docs/sandbox.md) и «Обновить все клиенты». «Показ»: всё, кратко (без строк инструментов) или только ответы (и заметка о прерванном ходе), отдельно 💭 размышления, и ход полный или сжатый (строки инструментов и 💭 сообщения хода в свёрнутой цитате, она раскрывается нажатием); у сессии в группе свой показ: переключатель «👤 Личка / 👥 Группа» во вкладке, по умолчанию в группе всё. «Звук» сообщений в личке: ответы и запросы (как раньше), всё (каждое сообщение сессии; служебные уведомления тихие) или ничего («ничего» глушит и запросы разрешений); тихие часы по вашему поясу, в них звук только у запросов разрешений и вопросов. Пояс задаётся фиксированным смещением: летнее время не учитывается, при переходе укажите пояс заново. Звук в группе не меняется. Настройки у каждого свои и хранятся в registry.json hub. Меню само не обновляется: ↻ показывает текущее состояние. У владельцев (CCTG_ALLOWED_USER_IDS) вторая строка вкладок: «👥 Люди» (ниже), «💻 Устройства» и «🛠 Hub». «Устройства»: все машины hub с владельцем и последним входом, 🗑 отзывает машину (второе нажатие подтверждает), «➕ Добавить устройство» присылает ниже строку установки с кодом, как /join, машина будет вашей. «Hub»: версия, время работы, число сессий и тем, 429 за сутки и уборка тем: выбрать возраст («сейчас» или 1, 7, 30, 90 дней), бот считает слоты и темы, 🧹 и второе нажатие удаляют темы слотов, завершённых раньше. Уборка общая на весь hub (темы всех людей, в личках и в группах), её делают только владельцы; слоты с сообщениями, ждущими Resume, и с открытым запросом или вопросом не трогаются; темы в группе без права удалять сообщения остаются как есть. Следующая сессия той же папки получает новую тему, а показ в группе у убранного слота пропадает: добавить сессию в группу нужно заново. Сообщение в личную тему убранного слота, пока она ждёт удаления и в папке ещё не было новой сессии, возвращает эту тему слоту; в остальных случаях (тема группы, папку уже заняла новая сессия) сообщение не передаётся, бот отвечает, что тему убрала уборка, и тема удаляется. Пока есть убранные слоты или темы в очереди на удаление, откат hub на v0.1.27 и старше нужен с правкой registry.json: docs/remote-hub.md, раздел «Обновление».
  • Несколько групп (шаг 6): сессию из лички можно показать в любой подключённой группе и в нескольких сразу. Когда групп больше одной, кнопка «👥 Группы…» на статусе, /share и /unshare в теме сессии и кнопка 👥 N… в меню присылают в тему сессии выбор: 👥 показать в группе, 🙈 убрать (с подтверждением, тема в этой группе удаляется). Режим упоминаний (📣/💬 в меню) один на сессию и действует во всех её группах; настройка показа «👥 Группа» тоже одна на все группы. Если сессия шла в основной группе только потому, что личка была недоступна, то после выбора основной группы её прежняя тема становится общей; пока выбор открыт (до 5 минут), в эту тему ничего не пишется, а если выбрана другая группа или время вышло, тема получает прощальное сообщение. Если hub перезапустился, пока выбор был открыт, прежняя тема остаётся как есть, без прощания, а выбор основной группы создаёт новую тему. У каждой группы, кроме основной, свой лимит сообщений Telegram и своя очередь сообщений в hub (ответы, поток хода, файлы, Resume): пауза после 429 в такой группе не задерживает ни личку, ни другие группы. Основная группа делит очередь hub с личками, как раньше. Если бота удалили из группы (в том числе из основной), сессии туда больше не пишут, пока его не вернут админом; это пауза, а не отмена показа: вернувшийся бот снова пишет в прежние темы. В других группах, кроме основной, /brief и /full работают только в теме сессии, показанной в этой группе, и без id сессии: в General и по id чужие сессии там не показываются.

Обновление

Команды выше берут установщик с main, он ставит последний релиз. Чтобы поставить конкретный релиз, замените main в ссылке на его тег (например v0.1.18).

  • Клиент: после обновления hub работающие сессии получают версию hub по кнопке «⬆️ Обновить» в теме: клиент сам скачивает сборку того же релиза из GitHub Releases, сверяет SHA256SUMS и ставит её на место старой. Конфиги и обёртку обновляет команда установки нового тега.
  • Hub: команда --hub нового тега (hub стоит на образе своего релиза, поэтому одного docker compose pull мало).

Удаление

curl -fsSL https://raw.githubusercontent.com/pockerhead/cctg/main/install.sh | sh -s -- --uninstall

Удаляет только то, что поставил установщик. Hub: та же строка с --hub --uninstall вместо --uninstall (останавливает контейнер, hub.env, ключ и состояние остаются), hub на этой машине: --hub --local --uninstall (останавливает hub и убирает автозапуск, ~/.cctg/hub остаётся). Пока стоит локальный hub, --uninstall клиента оставляет бинарник: hub работает из него.

About

Bridge between local Claude Code sessions and one private Telegram forum (Rust)

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages