Skip to content

Slack

Ongrid 는 별도의 Settings 패널에서 구성되는 두 가지 다른 모드 로 Slack 과 통합됩니다.

Mode용도Tokens
알림채널로 발화된 알림 pushwebhook URL 만
Socket Mode (IM)Slack 에서 에이전트와 대화xapp-… + xoxb-…

둘 다 운영 가능합니다. 상태를 공유하지 않습니다.

알림 (incoming webhook)

가장 간단한 통합. Slack 이 워크스페이스 특화 URL 을 https://hooks.slack.com/services/T…/B…/… 형태로 할당. Ongrid 가 그곳으로 알림 payload 를 POST 합니다.

payload 모양

Slack sender 는 하나의 notify.Messageattachments 포맷 으로 렌더링합니다 (Block Kit 대신 선택된 이유: 운영자가 severity 로 읽는 색상 사이드 레일을 가지며, 스키마가 JSON-flat 이고 보편적으로 지원됨):

json
{
  "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
    }
  ]
}

text 필드는 채워둔 채로 유지되어 클라이언트가 attachments 를 제거해도 Slack push / email digest 미리보기에 유용한 한 줄이 표시됩니다.

Severity → 색상

SeverityHex
critical#d92f2f
warning#f2c037
info#36a64f
(unknown)#6f7a87

Slack 클라이언트 버전 간 색조가 안정적이도록 고정된 hex (Slack 의 danger / warning 센티넬이 아님) 를 사용합니다.

설정

  1. Slack 에서: Apps → Incoming Webhooks → Add to Slack, 채널 선택.
  2. webhook URL 복사.
  3. Ongrid 에서: Settings → Channels → New → Provider = slackEndpoint 에 URL 붙여넣기.

폼의 Secret 필드는 Slack incoming webhook 에서는 무시됩니다. URL 이 자격 증명이며, 별도 서명 표면이 없습니다. 채널 빌더는 NewSlackSender 를 만들기 전에 secret 을 버립니다.

채널 테스트

Settings → Channels 의 Test 버튼은 합성된 notify.Message{Severity: "info", Subject: "Ongrid test", …} 를 보냅니다. 녹색 레일과 함께 attachment 가 대상 채널에 나타나면 채널이 배선된 것입니다.

Socket Mode (IM 브릿지)

Socket Mode 는 양방향 통합입니다. 인바운드 사용자 텍스트는 아웃바운드 WebSocket 으로 전달 (Telegram getUpdates 와 동일한 모양 — 공개 ingress 불필요) 되고, 매니저는 표준 Web API (chat.postMessage, chat.update) 로 응답합니다.

왜 Socket Mode 이고 Events API 가 아닌가

Events API 는 Slack 이 매니저에 인바운드로 도달해야 하므로 유효한 인증서를 가진 공개 HTTPS 엔드포인트가 필요합니다. 대부분의 Ongrid 배포는 프라이빗입니다. Socket Mode 는 매니저가 발신하는 지원되는 대안입니다 — HTTPS_PROXY / NO_PROXY 를 존중하며, NAT 와 GFW 뒤에서도 동작합니다. validator 는 Slack 에 mode != stream 을 거부:

slack only supports stream mode (Socket Mode)

필수 토큰

Slack 은 Socket Mode 에 두 토큰을 사용합니다. 스코프가 다릅니다.

TokenPrefix용도
App-level tokenxapp-…apps.connections.open (WebSocket URL 획득).
Bot tokenxoxb-…chat.postMessage, chat.update, 기타 모든 Web API 호출.

Ongrid 는 이를 im_apps.app_secret 내부의 JSON 객체로 저장합니다. ParseSecret 함수가 사전에 접두사를 검증하여 잘못 붙여넣은 토큰이 chat.postMessage 401 대신 깔끔한 에러로 표면화되도록 합니다.

json
{
  "app_token": "xapp-1-A0…",
  "bot_token": "xoxb-1234567890-1234567890-…"
}

필수 앱 스코프

Slack 앱 설정에서:

OAuth & Permissions → Bot Token Scopes

  • app_mentions:readapp_mention 이벤트 수신.
  • channels:history — 공개 채널의 message 이벤트 수신.
  • groups:history — 비공개 채널 동일.
  • im:history — DM 동일.
  • chat:writechat.postMessage / chat.update.

Socket Mode → Enable, 그런 다음 Basic Information → App-Level Tokens 에서 connections:write 스코프로 xapp-… 토큰 획득.

Event Subscriptions 활성화 (토글 on) 후 봇이 message.channels, message.groups, message.im, app_mention 을 구독해야 합니다. Slack 은 webhook 과 동일하게 Socket Mode 로 이것들을 보냅니다.

allow_from

봇과 대화 가능한 Slack 사용자 ID 의 콤마 구분 리스트. 사용자 ID 는 U (Enterprise 게스트의 경우 W) 로 시작. 최소 하나의 ID 필요:

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

Slack 에서 자기 ID 찾기: 아바타 클릭 → Profile 메뉴 → Copy member ID.

화이트리스트에 없는 발신자는 handleEvent 에서 조용히 버려집니다. 봇은 답신하지 않으며 자신의 존재를 확인하지 않습니다. 동작은 Telegram allow_from 을 미러링합니다.

스트리밍 응답 동작

화이트리스트 사용자가 메시지를 보내면:

  1. 브릿지가 placeholder 텍스트 (Working on it…) 와 함께 chat.postMessage 를 한 번 호출하고 반환된 ts 를 기록.
  2. 에이전트가 실행됨. 각 스트리밍 청크가 같은 (channel, ts)chat.update 를 트리거.
  3. 마지막 토큰이 같은 메시지에 최종 텍스트를 플러시.

Slack 은 no-op chat.update (동일 텍스트) 를 조용히 받아들입니다 — 400 을 반환하는 Telegram 과 달리. Slack 측에는 특별한 swallow 가 없습니다. stream.gosenderAdapter 참고.

봇 루프, 편집, 스레드 ID

  • bot_id != "" 인 메시지는 버려짐 — 매니저 자신의 답신이며 피드백 루프를 만들 것입니다.
  • non-empty subtype (message_changed, channel_join, …) 메시지는 버려짐.
  • thread_tsImThread.ImThreadID 로 저장되어 스레드 내 응답이 같은 대화 세션을 잇습니다.
  • Slack <@U…> 멘션 마크업은 stripMentions 가 단순 @U… 토큰으로 재작성하므로 모델은 메시지마다 users.info 왕복 없이 안정적인 참조를 봅니다.

설정

  1. Slack 앱 생성: api.slack.com → Create New App → from scratch, 워크스페이스 선택.
  2. Socket Mode 활성화, connections:write 로 app-level 토큰 생성. xapp-… 값 복사.
  3. 위에 나열된 Bot Token Scopes 설정.
  4. 워크스페이스에 앱 설치. xoxb-… Bot User OAuth Token 복사.
  5. Event Subscriptions 활성화, 위 이벤트들을 봇이 구독하도록 설정.
  6. Ongrid 에서: Settings → IM bridge → New → Provider = slack → Mode = stream → app_id (예: ongrid-bot), 두 토큰 JSON 을 App secret 에, 최소 하나의 Slack 사용자 ID 를 allow_from 에 붙여넣기.

스코프 추가 후 앱 재설치

Slack 은 이미 설치된 봇에 스코프를 소급 부여하지 않습니다. chat:write 를 추가한 후 chat.postMessagemissing_scope 를 반환하면 앱을 워크스페이스에 재설치하세요.

Keep-alive

스트림 루프는 20 초마다 ping (pingInterval). Slack 은 ~30 초 후 유휴 Socket Mode 연결을 닫습니다. ping 은 goroutine 에서 실행되므로 read 루프가 write 뒤에서 절대 블록되지 않습니다. Slack-initiated disconnect envelope 은 깔끔히 닫고 supervisor 가 즉시 (백오프 sleep 없이) 새로운 apps.connections.open URL 로 재연결합니다.