Slack
Ongrid s'intègre à Slack selon deux modes distincts, configurés sur des panneaux Settings séparés :
| Mode | À utiliser pour | Tokens |
|---|---|---|
| Notification | Pousser les alertes déclenchées dans un canal | Juste l'URL du webhook |
| Socket Mode (IM) | Parler à l'agent depuis Slack | xapp-… + xoxb-… |
Vous pouvez exécuter les deux. Ils ne partagent rien.
Notification (webhook entrant)
L'intégration la plus simple. Slack assigne une URL spécifique au workspace de la forme https://hooks.slack.com/services/T…/B…/… ; Ongrid POST le payload d'alerte à cette adresse.
À quoi ressemble le payload
Le sender Slack rend un notify.Message dans le format attachments (choisi plutôt que Block Kit parce qu'il porte le rail coloré latéral que les opérateurs lisent comme sévérité, et parce que le schéma est JSON à plat et universellement supporté) :
{
"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
}
]
}Le champ text reste rempli pour que les aperçus push / digest email de Slack montrent une ligne utile même quand le client supprime les attachments.
Sévérité → couleur
| Sévérité | Hex |
|---|---|
| critical | #d92f2f |
| warning | #f2c037 |
| info | #36a64f |
| (unknown) | #6f7a87 |
Hex épinglé (et non les sentinelles danger / warning de Slack) pour que la teinte soit stable à travers les versions de client Slack.
Configuration
- Dans Slack : Apps → Incoming Webhooks → Add to Slack, choisissez le canal.
- Copiez l'URL du webhook.
- Dans Ongrid : Settings → Channels → New → Provider =
slack→ collez l'URL dans Endpoint.
Le champ Secret du formulaire est ignoré pour les webhooks entrants Slack. L'URL est le credential ; il n'y a pas de surface de signature séparée. Le constructeur de canal supprime le secret avant de construire NewSlackSender.
Tester le canal
Le bouton Test sur Settings → Channels envoie un notify.Message{Severity: "info", Subject: "Ongrid test", …} synthétique. Si l'attachment apparaît dans le canal cible avec le rail vert, le canal est câblé.
Socket Mode (pont IM)
Socket Mode est l'intégration bidirectionnelle. Le texte utilisateur entrant est délivré sur un WebSocket sortant (pas d'ingress public requis — même forme que Telegram getUpdates), et le manager répond en utilisant l'API Web standard (chat.postMessage, chat.update).
Pourquoi Socket Mode et pas Events API
Events API requiert que Slack atteigne le manager en entrant, ce qui signifie un endpoint HTTPS public avec un certificat valide. La plupart des déploiements Ongrid sont privés. Socket Mode est l'alternative supportée où le manager compose en sortant — il honore HTTPS_PROXY / NO_PROXY et fonctionne derrière les NAT et le GFW. Le validateur rejette mode != stream pour Slack :
slack only supports stream mode (Socket Mode)
Tokens requis
Slack utilise deux tokens pour Socket Mode. Ils ont des scopes différents :
| Token | Préfixe | Utilisé pour |
|---|---|---|
| Token app-level | xapp-… | apps.connections.open (obtient une URL WebSocket). |
| Token bot | xoxb-… | chat.postMessage, chat.update, tous les autres appels API Web. |
Ongrid les stocke comme un objet JSON dans im_apps.app_secret. La fonction ParseSecret valide les préfixes d'avance pour qu'un token mal collé remonte comme une erreur propre au lieu d'un 401 de chat.postMessage.
{
"app_token": "xapp-1-A0…",
"bot_token": "xoxb-1234567890-1234567890-…"
}Scopes d'app requis
Dans la config de l'app Slack :
OAuth & Permissions → Bot Token Scopes
app_mentions:read— recevoir les événementsapp_mention.channels:history— recevoir les événementsmessagedans les canaux publics.groups:history— pareil pour les canaux privés.im:history— pareil pour les DM.chat:write—chat.postMessage/chat.update.
Socket Mode → Enable, puis Basic Information → App-Level Tokens avec le scope connections:write pour obtenir le token xapp-….
Event Subscriptions doit être activé (toggle on), avec le bot abonné à message.channels, message.groups, message.im, app_mention. Slack les livre sur Socket Mode de la même façon qu'il le ferait sur webhook.
allow_from
Une liste séparée par virgules d'ID utilisateurs Slack qui peuvent converser avec le bot. Les ID utilisateurs commencent par U (ou W pour les invités Enterprise). Au moins un ID est requis :
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
Trouvez votre propre ID dans Slack : cliquez sur votre avatar → Profile → le menu ⋯ → Copy member ID.
Les expéditeurs non listés sont silencieusement supprimés à handleEvent. Le bot ne répond pas — il ne confirme pas qu'il existe. Le comportement reflète Telegram allow_from.
Comportement de réponse en streaming
Quand un utilisateur autorisé envoie un message :
- Le pont appelle
chat.postMessageune fois avec un texte placeholder (Working on it…) et enregistre letsretourné. - L'agent s'exécute. Chaque chunk de streaming déclenche un
chat.updatesur le même(channel, ts). - Le token terminal flush le texte final dans le même message.
Slack accepte un chat.update no-op (même texte) silencieusement — contrairement à Telegram, qui retourne un 400. Il n'y a pas d'avalement spécial côté Slack. Voir senderAdapter dans stream.go.
Boucles de bot, edits et ID de thread
- Les messages avec
bot_id != ""sont supprimés — ce sont les propres réponses du manager et créeraient une boucle de feedback. - Les messages avec un
subtypenon vide (message_changed,channel_join, …) sont supprimés. thread_tsest stocké commeImThread.ImThreadIDpour qu'une réponse à l'intérieur d'un thread continue la même session de conversation.- Le markup de mention Slack
<@U…>est réécrit en un token@U…nu parstripMentionspour que le modèle voie une référence stable sans un round-tripusers.infopar message.
Configuration
- Créez une app Slack : api.slack.com → Create New App → from scratch, choisissez le workspace.
- Activez Socket Mode, générez un token app-level avec
connections:write. Copiez la valeurxapp-…. - Définissez les Bot Token Scopes listés ci-dessus.
- Installez l'app dans le workspace. Copiez le Bot User OAuth Token
xoxb-…. - Activez Event Subscriptions, abonnez le bot aux événements listés ci-dessus.
- Dans Ongrid : Settings → IM bridge → New → Provider =
slack→ Mode =stream→ collez l'app_id (le handle du bot, par ex.ongrid-bot), le JSON à deux tokens dans App secret, et au moins un ID utilisateur Slack dans allow_from.
Réinstallez l'app après avoir ajouté des scopes
Slack n'accorde pas rétroactivement de scopes aux bots déjà installés. Si chat.postMessage retourne missing_scope après que vous avez ajouté chat:write, réinstallez l'app dans le workspace.
Keep-alive
La boucle de stream ping toutes les 20s (pingInterval). Slack ferme les connexions Socket Mode inactives après ~30s. Le ping tourne dans une goroutine pour que la boucle de lecture ne soit jamais bloquée derrière une écriture. Une enveloppe disconnect initiée par Slack ferme proprement et le superviseur se reconnecte immédiatement (pas de sleep de backoff) avec une URL apps.connections.open fraîche.