Skip to content

Telegram

Telegram работает в обоих режимах:

РежимЧто он делает
NotificationОднонаправленный sendMessage от конвейера алертов.
IM bridgeДвусторонний чат с агентом через getUpdates long-poll.

Один и тот же bot-токен (8…:AA…, от @BotFather) питает оба — Telegram аутентифицирует через токен-в-пути. Нет отдельной схемы подписи.

Спроектировано для пользователей вне территории Feishu / DingTalk

Telegram — это IM-канал, который Ongrid рекомендует для не-китайских команд и для гибридных развёртываний, где операторы оказались на Telegram. Provider был добавлен в ADR-031.

Notification-режим

notify.Sender POST-ит {chat_id, text} на https://api.telegram.org/bot<TOKEN>/sendMessage. Credential — bot-токен (в пути); chat_id (числовой) — это целевой чат.

go
// internal/pkg/notify/webhook.go
NewTelegramSender(name, endpoint, chatID, client)

Где endpoint — буквальный URL …/bot<TOKEN>/sendMessage, а chatID — целевой чат (числовой ID пользователя для DM, или отрицательное число, которое Telegram даёт вам для группы).

Настройка:

  1. DM @BotFather/newbot → выберите username → скопируйте токен.
  2. Добавьте бота в целевой чат (group / channel) или DM-ните его один раз, чтобы он знал о вас.
  3. Получите chat ID. Самый быстрый способ: отправьте любое сообщение в чат, потом curl https://api.telegram.org/bot<TOKEN>/getUpdates и прочитайте result[0].message.chat.id.
  4. В Ongrid: Settings → Channels → New → Provider = telegram → Endpoint = https://api.telegram.org/bot<TOKEN>/sendMessage, chat_id = число из шага 3.

IM bridge-режим (двусторонний)

Входящее long-poll-ится из исходящего HTTPS-соединения manager. Поскольку вызов исходящий, Telegram работает за NAT, файрволами и HTTPS-прокси. setWebhook (альтернатива) потребовал бы, чтобы Telegram дотягивался до manager — это несовместимо с большинством private-cloud развёртываний и ненадёжно из материкового Китая.

stream — единственный режим

Валидатор отвергает что-либо ещё:

telegram only supports stream mode

Webhook-режим не выставлен в UI для Telegram.

Маппинг credentials

Строка im_apps переиспользует существующие столбцы — без изменений схемы:

Столбец im_appsЗначение в Telegram
provider"telegram"
mode"stream" (валидатор закрепляет это)
app_idUsername бота (например, ongridbot). Display + dedupe.
app_secretBotFather токен (8…:AA…). Шифруется at rest.
allow_fromОбязательный список числовых user ID через запятую.
verify_tokenНе используется.
encrypt_keyНе используется.

allow_from

Непустой список числовых Telegram user ID, которые могут разговаривать с ботом. Валидатор отбрасывает нечисловые токены (так что опечатанный alice приземляется как чистая required-ошибка, а не молчаливо пустой allowlist) и отвергает отрицательные токены (это group / supergroup chat ID и им не место в sender allowlist).

telegram requires allow_from — at least one numeric Telegram user ID (the bot is publicly reachable; an empty allowlist would let anyone command the agent)

Как найти числовой user ID:

  • DM @userinfobot, и он ответит вашим числовым ID.
  • Или отправьте сообщение в чате после регистрации, запустите curl https://api.telegram.org/bot<TOKEN>/getUpdates и прочитайте result[0].message.from.id.

Префиксы telegram: / tg: срезаются молча (OpenClaw compatibility), так что telegram:123456789 и 123456789 — одна и та же запись.

Молчаливое отбрасывание при промахе

Не-allowlisted отправители отбрасываются с WARN-логом:

text
telegram inbound from non-allowlisted sender — ignored
  user_id=42  user_name=alice  chat_id=42

Ответа нет. Placeholder'а нет. Бот даже не подтверждает, что существует. Это зеркалит OpenClaw allowFrom — ответ утёк бы, что agent-backed бот живёт на этом username, и подтолкнул бы людей прощупывать его.

Настройка

  1. DM @BotFather/newbot → выберите имя и уникальный username, заканчивающийся на bot. Скопируйте токен.
  2. (Опционально) @BotFather → /setprivacy → Disable, если хотите, чтобы бот видел сообщения в группах (по умолчанию только mention).
  3. Найдите свой числовой user ID через @userinfobot.
  4. В Ongrid: Settings → IM bridge → New → Provider = telegram → Mode = stream → App ID = username бота → App secret = токен BotFather → allow_from = ваш числовой ID (и любого тиммейта). Сохранить и Enable.
  5. DM боту с allowlisted-аккаунта. Бот отвечает placeholder'ом, потом редактирует его на месте по мере рассуждения агента.

История с прокси

Хост Telegram API (api.telegram.org) достижим в большинстве сетей, но заблокирован из материкового Китая. Provider использует zero-value http.Client, так что он учитывает HTTPS_PROXY / HTTP_PROXY / NO_PROXY из окружения manager — тот же proxy, который несёт getUpdates, несёт sendMessage.

В развёртываниях docker-compose сохраните прокси в docker-compose.override.yml:

yaml
services:
  manager:
    environment:
      HTTPS_PROXY: http://your-proxy:8080
      NO_PROXY: localhost,127.0.0.1,manager,mysql,prometheus,loki,tempo,grafana,qdrant

Не помещайте прокси в основной docker-compose.yml

Основной docker-compose.yml отгружается с каждым релизом. Override-файл — per-environment и переживает повторные запуски make package / install script.

Особенности, о которых стоит знать

message is not modified безвреден

Telegram возвращает HTTP 400 (message is not modified), когда payload editMessageText точно совпадает с текущим текстом сообщения. Прогрессивный streaming может повторить тот же чанк на throttled-тик или на финальном flush. Клиент проглатывает ровно эту строку ошибки и возвращает nil — всё остальное (400, 403, 5xx) всё ещё распространяется. См. EditMessageText.

Только один poller на бота

Telegram отвергает конкурентные getUpdates вызовы. StreamSupervisor форсит одного клиента на строку im_app. Если вы дублируете строку Telegram app, только одна успешно будет poll-ить — другая увидит 409 Conflict и откатится.

Rate-limit retries

429 Too Many Requests повторяется до 3 раз. Ожидание учитывает parameters.retry_after Telegram, capped на 60с. Ошибки 5xx повторяются с backoff 1с / 2с / 4с. Жёсткие 4xx (400/401/403) не повторяются — они всплывают на supervisor и, вероятно, указывают на проблему с токеном / chat-id. См. maxCallRetries.

Long-poll timeout

Server-side long poll ждёт 25с (pollTimeoutSec) с 10с буфера на client-side. Медленные сети просто видят более длинные poll-циклы; supervisor не делает busy-wait reconnect'ы.

Только текст

Стикеры, фото, voice-notes и файлы отбрасываются молча. Только события message.text запускают agent-ход. Bridge намеренно S1 (text in / text out); rich media — будущее расширение.