Telegram
Telegram funciona em ambos os modos:
| Modo | O que faz |
|---|---|
| Notificação | sendMessage de direção única do pipeline de alerta. |
| Ponte IM | Chat 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.
// 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:
- DM
@BotFather→/newbot→ escolha um username → copie o token. - Adicione o bot ao chat alvo (group / channel) ou DM uma vez para ele saber de você.
- Pegue o chat ID. O caminho mais rápido: envie qualquer mensagem no chat, então
curl https://api.telegram.org/bot<TOKEN>/getUpdatese leiaresult[0].message.chat.id. - 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_apps | Significado Telegram |
|---|---|
provider | "telegram" |
mode | "stream" (o validator fixa) |
app_id | Username do bot (ex.: ongridbot). Display + dedupe. |
app_secret | Token BotFather (8…:AA…). Criptografado em repouso. |
allow_from | Obrigatório lista separada por vírgula de IDs numéricos de usuário. |
verify_token | Não usado. |
encrypt_key | Nã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
@userinfobote 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 leiaresult[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:
telegram inbound from non-allowlisted sender — ignored
user_id=42 user_name=alice chat_id=42Nã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
- DM
@BotFather→/newbot→ escolha um nome e um username único terminando embot. Copie o token. - (Opcional)
@BotFather → /setprivacy → Disablese você quer que o bot veja mensagens em grupos (padrão é só-menção). - Encontre seu ID numérico de usuário via
@userinfobot. - 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. - 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:
services:
manager:
environment:
HTTPS_PROXY: http://your-proxy:8080
NO_PROXY: localhost,127.0.0.1,manager,mysql,prometheus,loki,tempo,grafana,qdrantNã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.