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 (числовой) — это целевой чат.
// internal/pkg/notify/webhook.go
NewTelegramSender(name, endpoint, chatID, client)Где endpoint — буквальный URL …/bot<TOKEN>/sendMessage, а chatID — целевой чат (числовой ID пользователя для DM, или отрицательное число, которое Telegram даёт вам для группы).
Настройка:
- DM
@BotFather→/newbot→ выберите username → скопируйте токен. - Добавьте бота в целевой чат (group / channel) или DM-ните его один раз, чтобы он знал о вас.
- Получите chat ID. Самый быстрый способ: отправьте любое сообщение в чат, потом
curl https://api.telegram.org/bot<TOKEN>/getUpdatesи прочитайтеresult[0].message.chat.id. - В 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_id | Username бота (например, ongridbot). Display + dedupe. |
app_secret | BotFather токен (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-логом:
telegram inbound from non-allowlisted sender — ignored
user_id=42 user_name=alice chat_id=42Ответа нет. Placeholder'а нет. Бот даже не подтверждает, что существует. Это зеркалит OpenClaw allowFrom — ответ утёк бы, что agent-backed бот живёт на этом username, и подтолкнул бы людей прощупывать его.
Настройка
- DM
@BotFather→/newbot→ выберите имя и уникальный username, заканчивающийся наbot. Скопируйте токен. - (Опционально)
@BotFather → /setprivacy → Disable, если хотите, чтобы бот видел сообщения в группах (по умолчанию только mention). - Найдите свой числовой user ID через
@userinfobot. - В Ongrid: Settings → IM bridge → New → Provider =
telegram→ Mode =stream→ App ID = username бота → App secret = токен BotFather →allow_from= ваш числовой ID (и любого тиммейта). Сохранить и Enable. - 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:
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 — будущее расширение.