Slack
O Ongrid se integra com Slack em dois modos distintos, configurados em painéis de Settings separados:
| Modo | Use para | Tokens |
|---|---|---|
| Notificação | Push de alertas disparados para um canal | Apenas a URL do webhook |
| Socket Mode (IM) | Falar com o agent do Slack | xapp-… + xoxb-… |
Você pode rodar ambos. Eles não compartilham nada.
Notificação (incoming webhook)
A integração mais simples. O Slack atribui uma URL específica do workspace no formato https://hooks.slack.com/services/T…/B…/…; o Ongrid faz POST do payload de alerta lá.
Como o payload se parece
O sender Slack renderiza um notify.Message no formato attachments (escolhido sobre Block Kit porque carrega o trilho de cor lateral que operadores leem como severity, e porque o schema é JSON-flat e universalmente suportado):
{
"text": "[CRITICAL] node-01 swap_high",
"attachments": [
{
"color": "#d92f2f",
"fallback": "[CRITICAL] node-01 swap_high",
"title": "node-01 swap_high",
"text": "swap_in_pages > 1000 for 5m",
"mrkdwn_in": ["text"],
"fields": [
{"title": "Severity", "value": "CRITICAL", "short": true},
{"title": "Source", "value": "alert", "short": true},
{"title": "Rule", "value": "swap_high","short": true},
{"title": "Incident", "value": "#1234", "short": true},
{"title": "Device", "value": "#7", "short": true},
{"title": "Dedupe key", "value": "alert:swap_high:device=7", "short": false}
],
"footer": "ongrid",
"ts": 1717012345
}
]
}O campo text continua populado para que previews de push / email digest do Slack mostrem um one-liner útil mesmo quando o client retira attachments.
Severity → cor
| Severity | Hex |
|---|---|
| critical | #d92f2f |
| warning | #f2c037 |
| info | #36a64f |
| (unknown) | #6f7a87 |
Hex fixado (não a sentinel danger / warning do Slack) para que o tom seja estável entre versões de client Slack.
Configuração
- No Slack: Apps → Incoming Webhooks → Add to Slack, escolha o canal.
- Copie a URL do webhook.
- No Ongrid: Settings → Channels → New → Provider =
slack→ cole a URL em Endpoint.
O campo Secret no form é ignorado para Slack incoming webhooks. A URL é a credencial; não há superfície separada de assinatura. O channel builder descarta o secret antes de construir NewSlackSender.
Teste o canal
O botão Test em Settings → Channels envia uma sintética notify.Message{Severity: "info", Subject: "Ongrid test", …}. Se o attachment aparece no canal alvo com o trilho verde, o canal está conectado.
Socket Mode (ponte IM)
Socket Mode é a integração two-way. Texto inbound de usuário é entregue por um WebSocket outbound (sem necessidade de ingress público — mesmo formato do Telegram getUpdates), e o manager responde usando a Web API padrão (chat.postMessage, chat.update).
Por que Socket Mode e não Events API
A Events API exige que o Slack alcance o manager inbound, o que significa um endpoint HTTPS público com um certificado válido. A maioria dos deployments Ongrid é privada. Socket Mode é a alternativa suportada onde o manager disca para fora — honra HTTPS_PROXY / NO_PROXY e funciona atrás de NATs e do GFW. O validator rejeita mode != stream para Slack:
slack only supports stream mode (Socket Mode)
Tokens obrigatórios
O Slack usa dois tokens para Socket Mode. Eles têm escopos diferentes:
| Token | Prefixo | Usado para |
|---|---|---|
| App-level token | xapp-… | apps.connections.open (obtém uma URL WebSocket). |
| Bot token | xoxb-… | chat.postMessage, chat.update, todas as outras chamadas da Web API. |
O Ongrid os armazena como um objeto JSON dentro de im_apps.app_secret. A função ParseSecret valida os prefixos de cara para que um token mal colado apareça como um erro limpo em vez de um 401 no chat.postMessage.
{
"app_token": "xapp-1-A0…",
"bot_token": "xoxb-1234567890-1234567890-…"
}Scopes de app obrigatórios
Na config do app Slack:
OAuth & Permissions → Bot Token Scopes
app_mentions:read— receber eventsapp_mention.channels:history— receber eventsmessageem canais públicos.groups:history— idem para canais privados.im:history— idem para DMs.chat:write—chat.postMessage/chat.update.
Socket Mode → Enable, então Basic Information → App-Level Tokens com scope connections:write para obter o token xapp-….
Event Subscriptions precisa estar habilitado (toggle on), com o bot inscrito em message.channels, message.groups, message.im, app_mention. O Slack distribui esses pelo Socket Mode da mesma forma que faria via webhook.
allow_from
Uma lista separada por vírgula de IDs de usuário Slack que podem conversar com o bot. IDs de usuário começam com U (ou W para Enterprise guests). Pelo menos um ID é obrigatório:
slack requires allow_from — at least one Slack user ID (e.g. UABC123, find via Profile → ⋯ → Copy member ID). Without it any workspace member could command a tool-equipped agent
Encontre seu próprio ID no Slack: clique seu avatar → Profile → o menu ⋯ → Copy member ID.
Senders não-allowlisted são silenciosamente descartados em handleEvent. O bot não responde — não confirma que existe. O comportamento espelha allow_from do Telegram.
Comportamento de resposta em streaming
Quando um usuário allowlisted envia uma mensagem:
- A ponte chama
chat.postMessageuma vez com um texto placeholder (Working on it…) e grava otsretornado. - O agent roda. Cada chunk de streaming dispara um
chat.updateno mesmo(channel, ts). - O token terminal faz flush do texto final na mesma mensagem.
O Slack aceita um chat.update no-op (mesmo texto) silenciosamente — diferente do Telegram, que retorna 400. Não há tratamento especial do lado Slack. Veja senderAdapter em stream.go.
Loops de bot, edits, e IDs de thread
- Mensagens com
bot_id != ""são descartadas — são as próprias respostas do manager e criariam um feedback loop. - Mensagens com qualquer
subtypenão-vazio (message_changed,channel_join, …) são descartadas. thread_tsé armazenado comoImThread.ImThreadIDpara que uma resposta dentro de uma thread continue a mesma sessão de conversa.- Marcação de menção Slack
<@U…>é reescrita para um token cru@U…porstripMentionspara que o modelo veja uma referência estável sem um round-tripusers.infopor mensagem.
Configuração
- Crie um app Slack: api.slack.com → Create New App → from scratch, escolha o workspace.
- Habilite Socket Mode, gere um app-level token com
connections:write. Copie o valorxapp-…. - Configure os Bot Token Scopes listados acima.
- Instale o app no workspace. Copie o Bot User OAuth Token
xoxb-…. - Habilite Event Subscriptions, inscreva o bot nos events listados acima.
- No Ongrid: Settings → IM bridge → New → Provider =
slack→ Mode =stream→ cole app_id (o handle do bot, ex.:ongrid-bot), o JSON de dois tokens em App secret, e pelo menos um Slack user ID em allow_from.
Reinstale o app após adicionar scopes
O Slack não concede scopes retroativamente a bots já instalados. Se chat.postMessage retornar missing_scope após você ter adicionado chat:write, reinstale o app no workspace.
Keep-alive
O loop de stream pinga a cada 20s (pingInterval). O Slack fecha conexões Socket Mode ociosas após ~30s. O ping roda em uma goroutine para que o loop de leitura nunca esteja bloqueado atrás de uma escrita. Um envelope disconnect iniciado pelo Slack fecha limpamente e o supervisor reconecta imediatamente (sem sleep de backoff) com uma URL apps.connections.open fresca.