Skip to content

Telegram

Telegram funciona em ambos os modos:

ModoO que faz
NotificaçãosendMessage de direção única do pipeline de alerta.
Ponte IMChat two-way com o agent via long-poll getUpdates.

O mesmo token de bot (8…:AA…, do @BotFather) dirige ambos — o Telegram autentica por token-no-path. Não há esquema de assinatura separado.

Projetado para usuários fora do território Feishu / DingTalk

Telegram é o canal IM que o Ongrid recomenda para times não-China e para deployments híbridos onde os operadores por acaso vivem no Telegram. O provider foi adicionado em ADR-031.

Modo notificação

Um notify.Sender faz POST de {chat_id, text} para https://api.telegram.org/bot<TOKEN>/sendMessage. A credencial é o token do bot (no path); o chat_id (numérico) é o chat de destino.

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

Onde endpoint é a URL literal …/bot<TOKEN>/sendMessage e chatID é o chat alvo (o ID numérico de um usuário para um DM, ou o número negativo que o Telegram te dá para um grupo).

Configuração:

  1. DM @BotFather/newbot → escolha um username → copie o token.
  2. Adicione o bot ao chat alvo (group / channel) ou DM uma vez para ele saber de você.
  3. Pegue o chat ID. O caminho mais rápido: envie qualquer mensagem no chat, então curl https://api.telegram.org/bot<TOKEN>/getUpdates e leia result[0].message.chat.id.
  4. No Ongrid: Settings → Channels → New → Provider = telegram → Endpoint = https://api.telegram.org/bot<TOKEN>/sendMessage, chat_id = o número do passo 3.

Modo ponte IM (two-way)

Inbound é long-polled da conexão HTTPS outbound do manager. Porque a chamada é outbound, o Telegram funciona atrás de NAT, firewalls, e proxies HTTPS. setWebhook (a alternativa) exigiria que o Telegram alcançasse o manager — isso é incompatível com a maioria dos deployments de private-cloud e não confiável de China continental.

stream é o único modo

O validator rejeita qualquer outra coisa:

telegram only supports stream mode

Modo webhook não é exposto na UI para Telegram.

Mapeamento de credenciais

A linha im_apps reaproveita colunas existentes — sem mudanças de schema:

Coluna im_appsSignificado Telegram
provider"telegram"
mode"stream" (o validator fixa)
app_idUsername do bot (ex.: ongridbot). Display + dedupe.
app_secretToken BotFather (8…:AA…). Criptografado em repouso.
allow_fromObrigatório lista separada por vírgula de IDs numéricos de usuário.
verify_tokenNão usado.
encrypt_keyNão usado.

allow_from

Uma lista não-vazia de IDs numéricos de usuário Telegram que podem conversar com o bot. O validator descarta tokens não-numéricos (para que um alice typo'd vire um erro limpo de campo obrigatório, não uma allowlist silenciosamente vazia) e rejeita tokens negativos (esses são IDs de chat de group / supergroup e não pertencem a uma allowlist de sender).

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)

Como achar um ID numérico de usuário:

  • DM @userinfobot e ele responde com seu ID numérico.
  • Ou envie uma mensagem no chat depois de se registrar, rode curl https://api.telegram.org/bot<TOKEN>/getUpdates, e leia result[0].message.from.id.

Prefixos telegram: / tg: são removidos silenciosamente (compatibilidade OpenClaw), então telegram:123456789 e 123456789 são a mesma entrada.

Silent drop em miss

Senders não-allowlisted são descartados com um log WARN:

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

Não há resposta. Não há placeholder. O bot nem confirma que existe. Isso espelha o allowFrom do OpenClaw — uma resposta vazaria que um bot baseado em agent vive nesse username e tentaria pessoas a sondá-lo.

Configuração

  1. DM @BotFather/newbot → escolha um nome e um username único terminando em bot. Copie o token.
  2. (Opcional) @BotFather → /setprivacy → Disable se você quer que o bot veja mensagens em grupos (padrão é só-menção).
  3. Encontre seu ID numérico de usuário via @userinfobot.
  4. No Ongrid: Settings → IM bridge → New → Provider = telegram → Mode = stream → App ID = o username do bot → App secret = o token BotFather → allow_from = seu ID numérico (e o de quaisquer colegas). Salve e Enable.
  5. DM o bot de uma conta allowlisted. O bot responde com um placeholder, então o edita no lugar conforme o agent raciocina.

A história do proxy

O host da API Telegram (api.telegram.org) é alcançável na maioria das redes mas bloqueado da China continental. O provider usa um http.Client de valor zero para que ele honre HTTPS_PROXY / HTTP_PROXY / NO_PROXY do environment do manager — o mesmo proxy que carrega getUpdates carrega sendMessage.

Em deployments docker-compose, persista o proxy em 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

Não coloque o proxy no docker-compose.yml principal

O docker-compose.yml principal é distribuído com cada release. Um arquivo de override é por-ambiente e sobrevive a re-runs de make package / script de instalação.

Pegadinhas que vale conhecer

message is not modified é inofensivo

O Telegram retorna HTTP 400 (message is not modified) quando um payload editMessageText casa exatamente com o texto atual da mensagem. Streaming progressivo pode repetir o mesmo chunk em um tick throttled ou no flush final. O client engole exatamente essa string de erro e retorna nil — qualquer outra coisa (400, 403, 5xx) ainda propaga. Veja EditMessageText.

Só um poller por bot

O Telegram rejeita chamadas concorrentes de getUpdates. O StreamSupervisor impõe um client por linha im_app. Se você duplicar uma linha de app Telegram, só uma fará poll com sucesso — a outra verá 409 Conflict e dará backoff.

Retries de rate-limit

429 Too Many Requests é retentado até 3 vezes. A espera honra o parameters.retry_after do Telegram, limitado a 60s. Erros 5xx retentam com backoff 1s / 2s / 4s. 4xx duro (400/401/403) não retenta — bolha para o supervisor e provavelmente indica um problema de token / chat-id. Veja maxCallRetries.

Timeout de long-poll

O long poll do lado server espera 25s (pollTimeoutSec) com um buffer de 10s do lado client. Redes lentas só veem ciclos de poll mais longos; o supervisor não está fazendo reconnects de busy-wait.

Apenas texto

Stickers, fotos, voice notes, e arquivos são descartados silenciosamente. Só events message.text dirigem um turn de agent. A ponte é intencionalmente S1 (texto in / texto out); rich media é uma expansão futura.