Slack
Ongrid 는 별도의 Settings 패널에서 구성되는 두 가지 다른 모드 로 Slack 과 통합됩니다.
| Mode | 용도 | Tokens |
|---|---|---|
| 알림 | 채널로 발화된 알림 push | webhook 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.Message 를 attachments 포맷 으로 렌더링합니다 (Block Kit 대신 선택된 이유: 운영자가 severity 로 읽는 색상 사이드 레일을 가지며, 스키마가 JSON-flat 이고 보편적으로 지원됨):
{
"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 → 색상
| Severity | Hex |
|---|---|
| critical | #d92f2f |
| warning | #f2c037 |
| info | #36a64f |
| (unknown) | #6f7a87 |
Slack 클라이언트 버전 간 색조가 안정적이도록 고정된 hex (Slack 의 danger / warning 센티넬이 아님) 를 사용합니다.
설정
- Slack 에서: Apps → Incoming Webhooks → Add to Slack, 채널 선택.
- webhook URL 복사.
- Ongrid 에서: Settings → Channels → New → Provider =
slack→ Endpoint 에 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 에 두 토큰을 사용합니다. 스코프가 다릅니다.
| Token | Prefix | 용도 |
|---|---|---|
| App-level token | xapp-… | apps.connections.open (WebSocket URL 획득). |
| Bot token | xoxb-… | chat.postMessage, chat.update, 기타 모든 Web API 호출. |
Ongrid 는 이를 im_apps.app_secret 내부의 JSON 객체로 저장합니다. ParseSecret 함수가 사전에 접두사를 검증하여 잘못 붙여넣은 토큰이 chat.postMessage 401 대신 깔끔한 에러로 표면화되도록 합니다.
{
"app_token": "xapp-1-A0…",
"bot_token": "xoxb-1234567890-1234567890-…"
}필수 앱 스코프
Slack 앱 설정에서:
OAuth & Permissions → Bot Token Scopes
app_mentions:read—app_mention이벤트 수신.channels:history— 공개 채널의message이벤트 수신.groups:history— 비공개 채널 동일.im:history— DM 동일.chat:write—chat.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 을 미러링합니다.
스트리밍 응답 동작
화이트리스트 사용자가 메시지를 보내면:
- 브릿지가 placeholder 텍스트 (
Working on it…) 와 함께chat.postMessage를 한 번 호출하고 반환된ts를 기록. - 에이전트가 실행됨. 각 스트리밍 청크가 같은
(channel, ts)에chat.update를 트리거. - 마지막 토큰이 같은 메시지에 최종 텍스트를 플러시.
Slack 은 no-op chat.update (동일 텍스트) 를 조용히 받아들입니다 — 400 을 반환하는 Telegram 과 달리. Slack 측에는 특별한 swallow 가 없습니다. stream.go 의 senderAdapter 참고.
봇 루프, 편집, 스레드 ID
bot_id != ""인 메시지는 버려짐 — 매니저 자신의 답신이며 피드백 루프를 만들 것입니다.- non-empty
subtype(message_changed,channel_join, …) 메시지는 버려짐. thread_ts는ImThread.ImThreadID로 저장되어 스레드 내 응답이 같은 대화 세션을 잇습니다.- Slack
<@U…>멘션 마크업은stripMentions가 단순@U…토큰으로 재작성하므로 모델은 메시지마다users.info왕복 없이 안정적인 참조를 봅니다.
설정
- Slack 앱 생성: api.slack.com → Create New App → from scratch, 워크스페이스 선택.
- Socket Mode 활성화,
connections:write로 app-level 토큰 생성.xapp-…값 복사. - 위에 나열된 Bot Token Scopes 설정.
- 워크스페이스에 앱 설치.
xoxb-…Bot User OAuth Token 복사. - Event Subscriptions 활성화, 위 이벤트들을 봇이 구독하도록 설정.
- Ongrid 에서: Settings → IM bridge → New → Provider =
slack→ Mode =stream→ app_id (예:ongrid-bot), 두 토큰 JSON 을 App secret 에, 최소 하나의 Slack 사용자 ID 를 allow_from 에 붙여넣기.
스코프 추가 후 앱 재설치
Slack 은 이미 설치된 봇에 스코프를 소급 부여하지 않습니다. chat:write 를 추가한 후 chat.postMessage 가 missing_scope 를 반환하면 앱을 워크스페이스에 재설치하세요.
Keep-alive
스트림 루프는 20 초마다 ping (pingInterval). Slack 은 ~30 초 후 유휴 Socket Mode 연결을 닫습니다. ping 은 goroutine 에서 실행되므로 read 루프가 write 뒤에서 절대 블록되지 않습니다. Slack-initiated disconnect envelope 은 깔끔히 닫고 supervisor 가 즉시 (백오프 sleep 없이) 새로운 apps.connections.open URL 로 재연결합니다.