🛡️ Скрепа

Боты в Скрепе: BotFather, E2E-диалоги и запуск gateway

Этот туториал поможет создать бота через @botfather, безопасно сохранить его реквизиты, запустить skrepa-bot-gateway и ответить на первое сообщение. Исходники Скрепы, Rust toolchain и protobuf-файлы для этого сценария не требуются.

Справочник методов, схем, ошибок и примеров запросов находится в интерактивной документации Bot API.

Что понадобится

Все команды ниже можно выполнить в пустом каталоге.

Как устроен gateway

skrepa-bot-gateway — локальный адаптер между кодом вашего бота и защищённым транспортом Скрепы. Приложение бота работает с привычным Telegram-compatible HTTP API. Gateway подключается к серверу по gRPC, регистрируется как отдельное устройство бота и берёт на себя E2E-криптографию.

flowchart LR
    U["Пользователь<br/>в клиенте Скрепы"] <-->|"E2E-DM"| S["Транспорт Скрепы"]
    S <-->|"gRPC:<br/>device inbox и ciphertext"| G["skrepa-bot-gateway"]
    G <-->|"Telegram-compatible HTTP:<br/>getUpdates и sendMessage"| B["Код вашего бота"]
    G --- D[("state_dir:<br/>identity, device, prekeys,<br/>Double Ratchet, durable queue")]

Путь входящего сообщения выглядит так:

  1. gateway получает зашифрованное сообщение из device inbox;
  2. расшифровывает его в сессии Double Ratchet;
  3. сохраняет update в локальную durable queue;
  4. код бота забирает update методом getUpdates.

Для исходящего сообщения код вызывает sendMessage. Gateway находит активные устройства адресата, создаёт отдельный ciphertext для каждого устройства и повторяет отправку до подтверждения сервера. Код бота при этом работает только с открытым текстом на локальной HTTP-границе.

Конфигурация gateway связывает четыре части:

Каталог state_dir должен переживать перезапуски. Размещайте его на постоянном диске, выдавайте доступ только процессу gateway и не подключайте один каталог одновременно к нескольким экземплярам. Bot token можно заменить через BotFather; identity recovery secret при ротации token остаётся прежним.

Gateway слушает два локальных адреса. Порт 8081 предоставляет обычный Bot API (getMe, getUpdates, sendMessage). Порт 8082 предназначен для дополнительных возможностей платформы. Для первого echo-бота достаточно порта 8081, однако healthcheck удобно выполнять на обоих.

Команды BotFather

КомандаРезультат
/start или /helpсправка
/newbotпошаговое создание бота
/mybotsсписок собственных ботов и их статусы
/token @usernameотзыв прежнего credential и выпуск нового token
/disable @usernameотключение бота и отзыв всех активных credentials
/cancelсброс незавершённого сценария

Туториал: создаём echo_bot

1. Откройте диалог

В клиенте Скрепы найдите @botfather и откройте личный диалог. Поиск не зависит от регистра; начальный символ @ можно опустить. В профиле должна отображаться отметка бота.

Если @botfather отсутствует в результатах или долго не отвечает, обновите клиент и повторите попытку. Сохраняющаяся ошибка означает проблему сервиса, о которой следует сообщить оператору стенда.

2. Запустите мастер

Отправьте:

/newbot

BotFather последовательно запросит:

  1. username длиной 3–32 ASCII-символа; разрешены латинские буквы, цифры и _;
  2. отображаемое имя длиной до 128 Unicode-символов;
  3. набор возможностей.

Для учебного бота диалог выглядит так:

Вы: /newbot
BotFather: Пришлите username нового бота…

Вы: echo_bot
BotFather: Теперь пришлите отображаемое имя бота.

Вы: Эхо
BotFather: Выберите возможности: default либо список dm,groups,channels через запятую.

Вы: default
BotFather: Готово: @echo_bot. … skrepa_bot_<bot-id-hex>_<secret-hex>
BotFather: Ключ восстановления identity: <64 hex символа>

Для текущего echo-сценария выберите default: этого достаточно для приватных сообщений. Значения groups и channels резервируют соответствующие возможности бота, хотя опубликованный gateway пока реализует только приватные текстовые диалоги.

3. Сохраните token и recovery secret

BotFather показывает два разных секрета:

Bot token показывается при создании и каждой ротации. Identity recovery secret выдаётся при создании и остаётся постоянным после ротации token. Сохраните оба значения в secret manager или файле с правами 0600. Не помещайте их в Git, shell history, issue tracker и логи.

При утрате или подозрении на компрометацию отправьте:

/token @echo_bot

Прежний token становится недействительным сразу после ответа.

4. Получите bot_id

Token имеет форму:

skrepa_bot_<64 hex символа bot_id>_<64 hex символа секрета>

Извлеките идентификатор локально:

set +x
read -rsp 'Bot token: ' SKREPA_BOT_TOKEN; printf '\n'
read -rsp 'Identity recovery secret: ' SKREPA_BOT_IDENTITY_RECOVERY_SECRET; printf '\n'
export SKREPA_BOT_TOKEN
export SKREPA_BOT_IDENTITY_RECOVERY_SECRET
export SKREPA_BOT_ID_HEX="$(printf '%s' "$SKREPA_BOT_TOKEN" | cut -d_ -f3)"
test "${#SKREPA_BOT_ID_HEX}" -eq 64

5. Скачайте gateway

Готовые дистрибутивы публикуются вместе с серверным релизом. На Mac с Apple Silicon скачайте нативный бинарный файл:

mkdir -p var/echo-bot/bin
cd var/echo-bot/bin

curl --fail-with-body -LO \
  https://skrepa.fomkin.org/download/bot-gateway/skrepa-bot-gateway-darwin-arm64.tar.gz
curl --fail-with-body -LO \
  https://skrepa.fomkin.org/download/bot-gateway/skrepa-bot-gateway-darwin-arm64.tar.gz.sha256
shasum -a 256 -c skrepa-bot-gateway-darwin-arm64.tar.gz.sha256
tar -xzf skrepa-bot-gateway-darwin-arm64.tar.gz
chmod 700 skrepa-bot-gateway
cd -

Для Linux amd64 и для остальных систем с Docker доступен образ в архиве:

mkdir -p var/echo-bot/image
cd var/echo-bot/image

curl --fail-with-body -LO \
  https://skrepa.fomkin.org/download/bot-gateway/skrepa-bot-gateway-linux-amd64.docker.tar.gz
curl --fail-with-body -LO \
  https://skrepa.fomkin.org/download/bot-gateway/skrepa-bot-gateway-linux-amd64.docker.tar.gz.sha256
shasum -a 256 -c skrepa-bot-gateway-linux-amd64.docker.tar.gz.sha256
gzip -dc skrepa-bot-gateway-linux-amd64.docker.tar.gz | docker load
cd -

Checksum-файл содержит имя соседнего архива. Поэтому команды проверки следует запускать из каталога, куда скачаны оба файла. На Linux shasum -a 256 -c можно заменить на sha256sum -c.

6. Создайте конфигурацию gateway

umask 077
mkdir -p var/echo-bot

jq -n \
  --arg endpoint "https://skrepa.fomkin.org" \
  --arg state_dir "${PWD}/var/echo-bot/state" \
  --arg bot_id_hex "${SKREPA_BOT_ID_HEX}" \
  --arg token "${SKREPA_BOT_TOKEN}" \
  --arg identity_recovery_secret "${SKREPA_BOT_IDENTITY_RECOVERY_SECRET}" \
  '{
    compatibility_profile_id: "telegram-v1-bot-api-9.6-minus-miniapps-business",
    bot_platform_endpoint: $endpoint,
    telegram_listen_addr: "127.0.0.1:8081",
    extensions_listen_addr: "127.0.0.1:8082",
    state_dir: $state_dir,
    bots: [{
      bot_id_hex: $bot_id_hex,
      token: $token,
      identity_recovery_secret_hex: $identity_recovery_secret,
      telegram_user_id: 7100001,
      username: "echo_bot",
      first_name: "Эхо"
    }]
  }' > var/echo-bot/gateway.json

chmod 600 var/echo-bot/gateway.json

telegram_user_id — положительный стабильный int64 для Telegram-compatible JSON. Текущая платформа его не назначает. identity_recovery_secret_hex — отдельный 256-битный recovery secret, который BotFather показывает вместе с первым token. Храните его в secret manager и сохраняйте прежним при ротации bot token. state_dir содержит стабильный bot_runtime_id и должен переживать перезапуски.

7. Запустите gateway

На Mac с Apple Silicon:

./var/echo-bot/bin/skrepa-bot-gateway serve var/echo-bot/gateway.json

Для Docker подготовьте конфигурацию с контейнерными адресами и каталогом state:

jq \
  '.telegram_listen_addr = "0.0.0.0:8081"
   | .extensions_listen_addr = "0.0.0.0:8082"
   | .state_dir = "/state"' \
  var/echo-bot/gateway.json > var/echo-bot/gateway.docker.json
chmod 600 var/echo-bot/gateway.docker.json

mkdir -p var/echo-bot/state
docker run --rm --name skrepa-echo-bot \
  --user "$(id -u):$(id -g)" \
  -p 127.0.0.1:8081:8081 \
  -p 127.0.0.1:8082:8082 \
  -v "${PWD}/var/echo-bot/gateway.docker.json:/config/gateway.json:ro" \
  -v "${PWD}/var/echo-bot/state:/state" \
  skrepa-bot-gateway:0.1.0 serve /config/gateway.json

Команда работает в foreground и пишет только служебные сведения о runtime. Для постоянного запуска перенесите те же параметры в systemd, Compose или вашу систему оркестрации. Секретный URL Bot API стоит исключить из access logs.

В другом терминале проверьте listeners и server-backed getMe:

curl --fail-with-body -sS http://127.0.0.1:8081/healthz | jq .
curl --fail-with-body -sS http://127.0.0.1:8082/healthz | jq .
curl --fail-with-body -sS \
  "http://127.0.0.1:8081/bot${SKREPA_BOT_TOKEN}/getMe" | jq .

Ожидаемый профиль:

{
  "ok": true,
  "result": {
    "id": 7100001,
    "is_bot": true,
    "first_name": "Эхо",
    "username": "echo_bot"
  }
}

После /token или /disable старый URL начинает возвращать HTTP 401. Gateway пока не перечитывает credential на лету: обновите gateway.json, перезапустите процесс и повторите getMe.

Gateway сам создаёт стабильное bot device, регистрирует подписанные prekey и запускает delivery loop. Входящие E2E-DM появляются в getUpdates:

curl --fail-with-body -sS \
  "http://127.0.0.1:8081/bot${SKREPA_BOT_TOKEN}/getUpdates?limit=100" | jq .

update_id подтверждается обычной Telegram-семантикой: следующий запрос передаёт offset, равный последнему обработанному update_id + 1. До этого момента update остаётся в локальной durable queue и переживает перезапуск gateway.

Ответ в приватный E2E-диалог отправляется через sendMessage. Возьмите chat.id из update:

curl --fail-with-body -sS \
  -H 'content-type: application/json' \
  -d '{"chat_id": 217020518514230019, "text": "Привет из бота"}' \
  "http://127.0.0.1:8081/bot${SKREPA_BOT_TOKEN}/sendMessage" | jq .

Gateway сверяет активные устройства адресата, шифрует отдельный ciphertext для каждого и сохраняет исходящее сообщение в local outbox до подтверждения платформы.

Что сохранять между перезапусками

В state_dir находятся криптографические сессии и локальные очереди конкретного экземпляра gateway. Это рабочие данные, сопоставимые с небольшой базой данных:

Потеря state_dir заставит gateway зарегистрировать новое устройство и построить криптографические сессии заново. Updates, которые уже были приняты с сервера и оставались только в локальной очереди, восстановить будет нельзя. Identity recovery secret сохраняет identity бота, однако локальные очереди он не заменяет.

Безопасность эксплуатации

Диагностика

СимптомЧто проверить
@botfather отсутствует в поискепроверьте написание, авторизацию клиента и соединение; затем сообщите оператору стенда
BotFather не отвечаетобновите клиент, повторите команду новым сообщением и проверьте соединение
gateway завершается при стартепроверьте JSON-конфигурацию, доступность bot_platform_endpoint и права на state_dir
gateway getMe возвращает 401token устарел, отозван, содержит лишние символы или бот отключён
регистрация устройства отклоненапроверьте соответствие bot token, recovery secret и каталога state одному боту
getUpdates пуст после сообщенияубедитесь, что пользователь написал этому боту, gateway продолжает работать и переданный offset корректен
sendMessage сообщает unknown private chatсначала должен прийти update из этого диалога; используйте его chat.id

Текущие ограничения

Что дальше

Замените ручные вызовы getUpdates циклом вашего приложения, сохраняйте последний подтверждённый update_id и обрабатывайте повторную доставку идемпотентно. Полный контракт запросов и ответов смотрите в Bot API Reference.