Wzorce integracji Slack dla alertów i przepływów pracy
Slack to interfejs użytkownika do przepływów pracy oraz warstwa dostarczania alertów.
Integracje ze Slackiem wydają się na pierwszy rzut oka oszaczająco proste, ponieważ wiadomość można wysłać w jednym żądaniu HTTP. Zainteresujące rzeczy zaczynają się wtedy, gdy chcesz, aby Slack był interaktywny i niezawodny.

Ta szczegółowa analiza traktuje Slacka jako trzy różne powierzchnie integracyjne:
- Zbiornik powiadomień dla jednostronnych alertów za pomocą przychodzących webhooków.
- Silnik przepływów pracy (workflow) za pomocą Workflow Buildera i niestandardowych kroków przepływu.
- Interfejs zdarzeń za pomocą przycisków Block Kit, poleceń slash oraz ładunków akcji (action payloads).
Ta strona opisuje, jak systemy przekraczają granicę do wspólnego interfejsu użytkownika, który może również emitować zdarzenia z powrotem do Twojej architektury, a nie filozofię alertów. Dla strategii alertowania i routingu zobacz Współczesny projekt systemów alertowania dla zespołów obserwowalności.
Powiązane lektury:
- Platformy czatu jako interfejsy systemowe we współczesnych systemach
- Wzór integracji Discorda dla alertów i pętli sterowania
- Współczesny projekt systemów alertowania dla zespołów obserwowalności
Kanoniczne ujęcie i umiejscowienie we wzorcach integracji
Slack to nie tylko miejsce, gdzie alerty umierają. Użyty mądrze, Slack staje się interfejsem systemowym, w którym wiadomości są artefaktami z stanem, a interakcje użytkownika stanowią zdarzenia.
Ta strona kanonicznie znajduje się pod /app-architecture/integration-patterns/slack/, ponieważ głównym pytaniem nie jest „czy powinniśmy generować alerty", ale „jaki jest kontrakt między naszym systemem a Slackiem".
Jeśli Twoje rozwiązanie wymaga któregokolwiek z poniższych, znajdujesz się w terenie wzorców integracji, a nie prostych powiadomień:
- Pętli decyzyjnej, gdzie zatwierdzenie przez człowieka blokuje akcję.
- Przepływu pracy (workflow), gdzie Slack zbiera kontekst i uruchamia kroki.
- Pętli zdarzeniowej, gdzie Slack emituje akcje, do których Twój system subskrybuje.
Platforma Slack intencjonalnie obsługuje zarówno jednostronną wymianę wiadomości, jak i dwukierunkową interakcję poprzez URL żądań i ładunki interakcji. Przychodzące webhooke są pierwszym sposobem na publikowanie ładunków JSON, w tym elementów budulcowych Block Kit, na kanał (Wysyłanie wiadomości przy użyciu przychodzących webhooków). Interaktywność jest dostarczana do Twojej aplikacji jako żądania HTTP POST do skonfigurowanego URL żądania, a te ładunki są kodowane jako formularz z polem JSON (Obsługa interakcji użytkownika).
Slack jako zbiornik powiadomień
Przychodzące webhooke to najszybsza droga do wartości dla alertów i aktualizacji statusu. Webhook to unikalny URL powiązany z instalacją aplikacji, do którego wysyłasz wiadomość JSON (Wysyłanie wiadomości przy użyciu przychodzących webhooków).
Opiniowany pogląd: webhooke są doskonałym domyślnym wyborem, gdy chcesz wysyłać wiadomości „wyślij i zapomnij" i nie potrzebujesz, aby Slack był powierzchnią sterowania. Webhooke są również doskonałym sposobem na odłączenie onboardingu od Twojej ostatecznej architektury aplikacji.
Slack jako silnik przepływów pracy
Workflow Builder istnieje, ponieważ praca dzieje się w czacie. Przepływy pracy mogą być proste lub złożone i mogą być podłączane do aplikacji (Poradnik Workflow Buildera).
Niestandardowe kroki przepływu pracy pozwalają na wystawienie Twoich systemów jako ponownie używane bloki budulcowe wewnątrz Workflow Buildera (Kroki przepływu pracy). Jest to inny kształt integracji niż boty na kanałach. Przenosi to Twoją integrację bliżej „narzędzi wewnątrz Slacka" niż „wiadomości z zewnątrz".
Opiniowany pogląd: jeśli Twoja organizacja już myśli w kategoriach przepływów pracy i zatwierdzeń, kroki przepływu pracy mogą wydawać się bardziej naturalne niż dedykowane boty.
Slack jako interfejs zdarzeń
Block Kit zamienia wiadomość w powierzchnię UI (Block Kit). Interaktywne komponenty, takie jak przyciski, generują ładunki akcji, zazwyczaj ładunki block_actions, które są wysyłane do Twojej aplikacji, gdy użytkownik kliknie (ładunek block_actions).
Przyciski mają jawnie zdefiniowane identyfikatory action_id i opcjonalną wartość, a muszą być umieszczone w blokach section lub actions (Element przycisku). Kiedy projektujesz wiadomość z przyciskiem, projektujesz źródło zdarzeń.
To tutaj tematy FAQ, takie jak weryfikacja żądań, wymagane zakresy (scopes) i bezpieczne wyzwalacze wewnętrzne, stają się centrum projektu.
Wzorce architektury, które skalują się
Przepływ webhooka dla jednostronnego alertowania
[service] -> [alert formatter] -> [slack incoming webhook] -> [channel]
Przychodzące webhooke akceptują ładunki JSON i obsługują układy Block Kit (Wysyłanie wiadomości przy użyciu przychodzących webhooków).
Kiedy FAQ pyta o najszybszy sposób wysyłania alertów, zazwyczaj jest to ten sposób.
Przepływ z brokerem z kolejką dla niezawodności i przeciwdziałania przeciążeniom
[services] -> [queue topic] -> [slack dispatcher] -> [slack api]
| |
| +-> [rate limit handler]
+-> [dead letter queue]
Limity współczynników (rate limits) Slacka dotyczą API opartych na HTTP, w tym przychodzących webhooków, a Slack zwraca HTTP 429 z nagłówkiem Retry-After, gdy przekraczasz limity (Limity współczynników).
Opiniowany pogląd: jeśli wysyłasz alerty bezpośrednio z każdej usługi, pierwszy incydent zamienia się w rozproszony atak zaprzepaszenia usługi (DDoS) na Twoją własną integrację ze Slackiem. Dyspozyter za kolejką zazwyczaj tworzy spokojniejszą architekturę.
Wzór automatyzacji przepływu pracy z zatwierdzeniami
[alert] -> [slack message with button] -> [button click]
-> [action payload] -> [approval handler] -> [internal API] -> [update message]
Interaktywność Slacka wymaga skonfigurowania URL żądania (Request URL) i włączenia interaktywności. Slack wysyła ładunki interakcji jako application/x-www-form-urlencoded z parametrem payload zawierającym JSON, a musisz odpowiedzieć HTTP 200 w ciągu 3 sekund (Obsługa interakcji użytkownika).
Jest to wzór stojący za elementem FAQ dotyczącym bezpiecznego uruchamiania wewnętrznych akcji.
Wykres przepływu interakcji ze Slackiem

Webhook vs aplikacja i mechanika implementacji
Zalecane biblioteki
Go:
- slack-go/slack dla Web API i struktur Block Kit (repo slack-go/slack, dokumentacja pkg.go.dev)
Python:
- slack_sdk (Python Slack SDK) dla klientów Web API, pomocników podpisu i infrastruktury ponawiania (dokumentacja Python Slack SDK, repo python-slack-sdk)
Podejście webhooka vs bota aplikacji
Praktyczne porównanie:
| Funkcja | Przychodzący webhook | Aplikacja Slacka z tokenem bota |
|---|---|---|
| Publikacja wiadomości | Tak | Tak |
| Publikacja układów Block Kit | Tak | Tak |
| Otrzymywanie kliknięć przycisków | Tylko jeśli powiązany z aplikacją z interaktywnością | Tak |
| Polecenia slash | Nie | Tak |
| Kroki przepływu pracy | Nie | Tak |
| Powierzchnia bezpieczeństwa | Tajemnica URL webhooka | Tokeny OAuth plus sekret podpisu |
| Najlepsze dopasowanie | Jednostronne alerty | Przepływy pracy, zatwierdzenia, interaktywne UI |
Slack jawnie obsługuje układy Block Kit z przychodzącymi webhookami (Wysyłanie wiadomości przy użyciu przychodzących webhooków). Interaktywność jest konfigurowana dla każdej aplikacji i dostarczana do URL żądania (Obsługa interakcji użytkownika).
Opiniowany pogląd: webhooke są świetnym pierwszym kamieniem milowym, ale tak soon, jak chcesz, aby Slack był powierzchnią sterowania, budujesz aplikację. Nie udawaj inaczej.
Zakresy i uprawnienia
Zakresy (scopes) Slacka definiują, co Twoja aplikacja może robić. Istnieje centralne odniesienie zakresów i strony dla poszczególnych zakresów (Odniesienie zakresów). Dla wysyłania wiadomości przez Web API, chat:write jest kanonicznym zakresem (zakres chat:write).
Dla poleceń slash zazwyczaj potrzebujesz zakresu commands i skonfigurowanego URL żądania polecenia (polecenia są częścią dokumentacji interaktywności, a każde polecenie ma swój własny URL żądania) (Obsługa interakcji użytkownika).
Uwaga FAQ: dostarczanie ładunku przycisku nie jest „zakresem", to ustawienie aplikacji. Twoja aplikacja otrzymuje ładunki, gdy interaktywność jest włączona i URL żądania jest ustawiony, ale aktualizacja wiadomości nadal zazwyczaj wymaga chat:write.
Limity współczynników i ponawianie
Limity współczynników Slacka zwracają HTTP 429 i zawierają Retry-After w sekundach, a dotyczy to API opartych na HTTP, w tym przychodzących webhooków (Limity współczynników).
W praktyce:
- szanuj Retry-After
- stosuj cofanie (backoff) z jitterem dla przejściowych błędów 5xx
- centralizuj dostarczanie do Slacka w dyspozyterze, gdy rośnie wolumen
Idempotentność i deduplikacja
Slack oczekuje potwierdzenia dla ładunków interakcji w ciągu 3 sekund, w przeciwnym razie użytkownicy widzą błąd, a Slack może ponowić dostarczenie w zależności od funkcji (Obsługa interakcji użytkownika). Dla API Zdarzeń Slack jawnie dostarcza nagłówki metadanych ponowienia x-slack-retry-num (API Zdarzeń).
Nawet bez jawnego ponawiania, duplikaty występują, ponieważ użytkownicy dwukrotnie klikają i ponieważ systemy rozproszone przesyłają ponownie. Jeśli Twój przycisk uruchamia wewnętrzną akcję, traktuj kliknięcia jako zdarzenia „co najmniej raz" i deduplikuj.
Praktyczna strategia idempotentności dla zatwierdzeń:
- klucz idempotentności = team_id + channel_id + message_ts + action_id + user_id
- przechowuj klucz w Redis z TTL pasującym do okna przepływu pracy
- wewnętrzne API akcji również wymusza idempotentność, nie tylko handler Slacka
Podstawy bezpieczeństwa i weryfikacja żądań
Slack podpisuje żądania do Twojego serwera używając Twojego sekretu podpisu aplikacji. Slack wysyła nagłówki X-Slack-Signature i X-Slack-Request-Timestamp, a Slack zaleca odrzucanie żądań starszych niż pięć minut, aby zapobiec atakom odtwarzania (Weryfikacja żądań z Slacka).
Dwa pułapki, które pojawiają się w prawdziwych recenzjach kodu:
- Musisz obliczyć podpis nad surowym ciałem żądania, przed parsowaniem JSON lub dekodowaniem formularza (Weryfikacja żądań z Slacka).
- Musisz potwierdzić interaktywne ładunki w ciągu 3 sekund, więc wykonuj ciężką pracę asynchronicznie i używaj response_url do komunikowania wyników (Obsługa interakcji użytkownika).
Python Slack SDK zawiera narzędzie weryfikujące podpis żądania w kodzie i dokumentacji (python-slack-sdk signature verifier).
Projekt wiadomości i interakcji
Szablon wiadomości alertu
Jeśli chcesz, aby Slack działał jak interfejs systemowy, strukturuj wiadomości tak, aby decyzje były oczywiste. Szablon wiadomości, który dobrze działa w różnych zespołach:
- tytuł
- powaga
- kontekst
- sugestia akcji
- linki
Minimalny szablon:
tytuł: wysoka stopa błędów w checkout powaga: warn kontekst: service=checkout env=prod region=us-east sugestia akcji: kliknij Approve restart, aby uruchomić bezpieczny restart
Przykład ładunku przychodzącego webhooka
Przychodzące webhooke akceptują ładunki JSON i mogą zawierać bogate układy używające Block Kit (Wysyłanie wiadomości przy użyciu przychodzących webhooków).
{
"text": "wysoka stopa błędów w checkout",
"blocks": [
{
"type": "header",
"text": { "type": "plain_text", "text": "wysoka stopa błędów w checkout" }
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*powaga*\\nwarn" },
{ "type": "mrkdwn", "text": "*kontekst*\\nservice=checkout env=prod region=us-east" }
]
},
{
"type": "section",
"text": { "type": "mrkdwn", "text": "*sugestia akcji*\\nKliknij Approve, aby uruchomić bezpieczny restart." }
}
]
}
Projektowanie przycisków i identyfikatorów
Przyciski muszą znajdować się w blokach section lub actions i zawierać action_id oraz opcjonalną wartość (Element przycisku). action_id jest Twoim kluczem routingu. value jest Twoim ładunkiem. Razem stanowią one Twój schemat zdarzeń.
Opiniowany pogląd: wybieraj wartości action_id jak stabilne punkty końcowe API. Nazwy takie jak „approve_restart" starzeją się lepiej niż „button_1".
Obsługa ładunków interakcji, response_url i timing
Slack wysyła ładunki interakcji do Twojego URL żądania jako dane zakodowane formularza z parametrem payload zawierającym JSON. Ładunek zawiera pole type definiujące źródło, takie jak block_actions dla kliknięć przycisków (Obsługa interakcji użytkownika, Ładunki interakcji).
Musisz zwrócić HTTP 200 w ciągu 3 sekund dla odpowiedzi potwierdzenia (Obsługa interakcji użytkownika). Użyj response_url do aktualizacji oryginalnej wiadomości lub odpowiedzi na kanału lub w wątku, a Slack ogranicza użycie response_url do pięciu razy w ciągu trzydziestu minut (Obsługa interakcji użytkownika).
To ograniczenie czasowe jest ograniczeniem projektowym. Zmusza Cię do odłączenia „potwierdzenia" od „wykonania pracy".
Wzorce interakcji pasujące do Slacka
- Przyciski w Block Kit dla zatwierdzeń i rozgałęziania.
- Polecenia slash dla jawnego zamiaru użytkownika i parametrów.
- Kroki przepływu pracy dla powtarzalnych procesów biznesowych w Workflow Builder (Kroki przepływu pracy).
- Skróty i okna modalne, gdy potrzebujesz strukturalnego wprowadzenia, z ograniczeniami trigger_id opisanymi w dokumentacji interaktywności (Obsługa interakcji użytkownika).
Przykłady w Go i Pythonie
Uwaga wydawcy: te mogą być podzielone na dedykowane strony przykładów:
/app-architecture/integration-patterns/slack/go-example/app-architecture/integration-patterns/slack/python-example
Przykłady priorytetyzują jedną rzecz, która utrzymuje systemy stabilne:
- weryfikuj podpisy Slacka
- potwierdzaj w ciągu trzech sekund
- deduplikuj akcje
- uruchamiaj wewnętrzne żądanie HTTP POST
- opcjonalnie aktualizuj Slacka używając response_url
Przykład Go: wyślij alert i obsłuż zatwierdzenie przycisku
Wymagania wstępne:
- Aplikacja Slacka z włączoną interaktywnością i skonfigurowanym URL żądania (Obsługa interakcji użytkownika).
- Token bota z zakresem chat:write (zakres chat:write).
- Sekret podpisu do weryfikacji żądań (Weryfikacja żądań z Slacka).
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"log"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/slack-go/slack"
)
type BlockActionPayload struct {
Type string `json:"type"`
Team struct{ ID string `json:"id"` } `json:"team"`
User struct{ ID string `json:"id"` } `json:"user"`
Channel struct {
ID string `json:"id"`
} `json:"channel"`
Message struct {
Ts string `json:"ts"`
} `json:"message"`
ResponseURL string `json:"response_url"`
Actions []struct {
ActionID string `json:"action_id"`
Value string `json:"value"`
Type string `json:"type"`
} `json:"actions"`
}
type InternalAction struct {
Action string `json:"action"`
TeamID string `json:"team_id"`
ChannelID string `json:"channel_id"`
MessageTS string `json:"message_ts"`
UserID string `json:"user_id"`
Value string `json:"value"`
}
var (
// W produkcji przechowuj to w Redis z TTL
seenMu sync.Mutex
seen = map[string]time.Time{}
ttl = 10 * time.Minute
)
func main() {
botToken := os.Getenv("SLACK_BOT_TOKEN")
signingSecret := os.Getenv("SLACK_SIGNING_SECRET")
channelID := os.Getenv("SLACK_CHANNEL_ID")
internalURL := os.Getenv("INTERNAL_API_URL")
listenAddr := os.Getenv("LISTEN_ADDR") // np. :8080
if botToken == "" || signingSecret == "" || channelID == "" || internalURL == "" || listenAddr == "" {
log.Fatal("brak zmiennych środowiskowych SLACK_BOT_TOKEN SLACK_SIGNING_SECRET SLACK_CHANNEL_ID INTERNAL_API_URL LISTEN_ADDR")
}
api := slack.New(botToken)
// Wyślij wiadomość alertu z przyciskiem zatwierdzenia.
// Przyciski są interaktywnymi elementami Block Kit z action_id i value.
// Zobacz dokumentację elementu przycisku Block Kit Slacka.
blocks := slack.Blocks{
BlockSet: []slack.Block{
slack.NewHeaderBlock(slack.NewTextBlockObject("plain_text", "wysoka stopa błędów w checkout", false, false)),
slack.NewSectionBlock(
slack.NewTextBlockObject("mrkdwn", "*powaga*\\nwarn\\n*kontekst*\\nservice=checkout env=prod", false, false),
nil,
nil,
),
slack.NewActionBlock(
"actions_1",
slack.NewButtonBlockElement("approve_restart", "restart", slack.NewTextBlockObject("plain_text", "Zatwierdź restart", false, false)),
),
},
}
_, ts, err := api.PostMessage(channelID, slack.MsgOptionBlocks(blocks.BlockSet...))
if err != nil {
log.Fatalf("PostMessage failed: %v", err)
}
log.Printf("posted alert message_ts=%s", ts)
// Endpoint interaktywności
http.HandleFunc("/slack/actions", func(w http.ResponseWriter, r *http.Request) {
rawBody, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "read body failed", http.StatusBadRequest)
return
}
r.Body.Close()
// Zweryfikuj podpis żądania Slacka na surowym ciele i znaczniku czasu.
// Zobacz dokumentację weryfikacji żądań Slacka.
if !verifySlackRequest(r.Header, rawBody, signingSecret) {
http.Error(w, "invalid signature", http.StatusUnauthorized)
return
}
// Slack wysyła application/x-www-form-urlencoded z payload=JSON
vals, err := url.ParseQuery(string(rawBody))
if err != nil {
http.Error(w, "bad form body", http.StatusBadRequest)
return
}
payloadStr := vals.Get("payload")
if payloadStr == "" {
http.Error(w, "missing payload", http.StatusBadRequest)
return
}
var p BlockActionPayload
if err := json.Unmarshal([]byte(payloadStr), &p); err != nil {
http.Error(w, "bad payload json", http.StatusBadRequest)
return
}
// Potwierdź w ciągu 3 sekund. Wykonuj prawdziwą pracę asynchronicznie i używaj response_url do aktualizacji.
// Zobacz dokumentację interaktywności Slacka dotyczącą czasu potwierdzenia.
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(""))
go func() {
if p.Type != "block_actions" || len(p.Actions) == 0 {
return
}
a := p.Actions[0]
if a.ActionID != "approve_restart" {
return
}
// Deduplikuj zatwierdzenia
key := strings.Join([]string{p.Team.ID, p.Channel.ID, p.Message.Ts, p.User.ID, a.ActionID, a.Value}, "|")
if !tryOnce(key) {
return
}
req := InternalAction{
Action: "approve_restart",
TeamID: p.Team.ID,
ChannelID: p.Channel.ID,
MessageTS: p.Message.Ts,
UserID: p.User.ID,
Value: a.Value,
}
if err := postJSON(internalURL, req); err != nil {
log.Printf("internal action failed: %v", err)
_ = replyViaResponseURL(p.ResponseURL, "action failed, check logs")
return
}
_ = replyViaResponseURL(p.ResponseURL, "approval received, internal action triggered")
}()
})
log.Printf("listening on %s", listenAddr)
log.Fatal(http.ListenAndServe(listenAddr, nil))
}
func tryOnce(key string) bool {
now := time.Now()
seenMu.Lock()
defer seenMu.Unlock()
for k, t := range seen {
if now.Sub(t) > ttl {
delete(seen, k)
}
}
if _, ok := seen[key]; ok {
return false
}
seen[key] = now
return true
}
func verifySlackRequest(h http.Header, body []byte, signingSecret string) bool {
ts := h.Get("X-Slack-Request-Timestamp")
sig := h.Get("X-Slack-Signature")
if ts == "" || sig == "" {
return false
}
tsInt, err := strconv.ParseInt(ts, 10, 64)
if err != nil {
return false
}
// Odrzuć żądania starsze niż 5 minut, aby zmniejszyć ryzyko odtworzenia.
if time.Since(time.Unix(tsInt, 0)) > 5*time.Minute {
return false
}
base := "v0:" + ts + ":" + string(body)
mac := hmac.New(sha256.New, []byte(signingSecret))
mac.Write([]byte(base))
sum := hex.EncodeToString(mac.Sum(nil))
expected := "v0=" + sum
return hmac.Equal([]byte(expected), []byte(sig))
}
func postJSON(url string, body any) error {
b, err := json.Marshal(body)
if err != nil {
return err
}
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(b))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
c := &http.Client{Timeout: 5 * time.Second}
res, err := c.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode >= 300 {
return io.ErrUnexpectedEOF
}
return nil
}
func replyViaResponseURL(responseURL string, text string) error {
if responseURL == "" {
return nil
}
// response_url akceptuje ładunki JSON i może publikować efemeryczne domyślnie.
b, _ := json.Marshal(map[string]string{
"text": text,
})
req, _ := http.NewRequest(http.MethodPost, responseURL, bytes.NewReader(b))
req.Header.Set("Content-Type", "application/json")
c := &http.Client{Timeout: 5 * time.Second}
res, err := c.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}
Przykład Pythona: wyślij alert i obsłuż zatwierdzenie przycisku
Wymagania wstępne:
- Aplikacja Slacka z włączoną interaktywnością i skonfigurowanym URL żądania (Obsługa interakcji użytkownika).
- Token bota z zakresem chat:write (zakres chat:write).
- Sekret podpisu do weryfikacji żądań (Weryfikacja żądań z Slacka).
import os
import json
import time
import threading
import requests
from flask import Flask, request, make_response
from slack_sdk import WebClient
from slack_sdk.signature import SignatureVerifier
SLACK_BOT_TOKEN = os.environ["SLACK_BOT_TOKEN"]
SLACK_SIGNING_SECRET = os.environ["SLACK_SIGNING_SECRET"]
SLACK_CHANNEL_ID = os.environ["SLACK_CHANNEL_ID"]
INTERNAL_API_URL = os.environ["INTERNAL_API_URL"]
client = WebClient(token=SLACK_BOT_TOKEN)
verifier = SignatureVerifier(signing_secret=SLACK_SIGNING_SECRET)
app = Flask(__name__)
# W produkcji przechowuj te w Redis
_seen = {}
_TTL_SECONDS = 600
def try_once(key: str) -> bool:
now = int(time.time())
expired = [k for k, t in _seen.items() if now - t > _TTL_SECONDS]
for k in expired:
_seen.pop(k, None)
if key in _seen:
return False
_seen[key] = now
return True
def post_internal_action(payload: dict) -> None:
requests.post(INTERNAL_API_URL, json=payload, timeout=5)
def reply_via_response_url(response_url: str, text: str) -> None:
if not response_url:
return
requests.post(response_url, json={"text": text}, timeout=5)
def send_alert_with_button() -> None:
blocks = [
{"type": "header", "text": {"type": "plain_text", "text": "wysoka stopa błędów w checkout"}},
{"type": "section", "text": {"type": "mrkdwn", "text": "*powaga*\\nwarn\\n*kontekst*\\nservice=checkout env=prod"}},
{
"type": "actions",
"block_id": "actions_1",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "Zatwierdź restart"},
"action_id": "approve_restart",
"value": "restart"
}
]
}
]
# chat.postMessage wymaga zakresu chat:write.
client.chat_postMessage(channel=SLACK_CHANNEL_ID, text="wysoka stopa błędów w checkout", blocks=blocks)
@app.post("/slack/actions")
def slack_actions():
raw_body = request.get_data() # Slack zaleca weryfikację surowego ciała przed parsowaniem
if not verifier.is_valid_request(raw_body, request.headers):
return make_response("invalid signature", 401)
# Slack wysyła application/x-www-form-urlencoded z polem payload zawierającym JSON.
payload_str = request.form.get("payload", "")
if not payload_str:
return make_response("missing payload", 400)
payload = json.loads(payload_str)
# Potwierdź w ciągu 3 sekund. Użytkownik Slacka widzi błędy, jeśli tego nie zrobisz.
# Zobacz dokumentację interaktywności Slacka dotyczącą potwierdzenia.
resp = make_response("", 200)
def work():
if payload.get("type") != "block_actions":
return
actions = payload.get("actions", [])
if not actions:
return
a = actions[0]
if a.get("action_id") != "approve_restart":
return
team_id = payload.get("team", {}).get("id", "")
channel_id = payload.get("channel", {}).get("id", "")
user_id = payload.get("user", {}).get("id", "")
message_ts = payload.get("message", {}).get("ts", "")
value = a.get("value", "")
key = "|".join([team_id, channel_id, message_ts, user_id, "approve_restart", value])
if not try_once(key):
return
internal_payload = {
"action": "approve_restart",
"team_id": team_id,
"channel_id": channel_id,
"message_ts": message_ts,
"user_id": user_id,
"value": value,
}
try:
post_internal_action(internal_payload)
reply_via_response_url(payload.get("response_url", ""), "approval received, internal action triggered")
except Exception:
reply_via_response_url(payload.get("response_url", ""), "action failed, check logs")
threading.Thread(target=work, daemon=True).start()
return resp
if __name__ == "__main__":
send_alert_with_button()
app.run(host="0.0.0.0", port=int(os.getenv("PORT", "8080")))
Uwagi operacyjne: routing, UX, bezpieczeństwo, linki i SEO
Kiedy używać Slacka vs narzędzi do powiadamiania vs Discorda
Ta strona dotyczy mechaniki. Routing to strategia. Nadal, granica jest łatwa do opisania.
| Kanał | Najlepsze dla | Tryb awarii |
|---|---|---|
| PagerDuty lub równoważne | Pilne wpływy na użytkownika wymagające natychmiastowej reakcji | Ludzie śpią przez Slack |
| Slack | Koordynacja, zatwierdzenia, wykonywanie przepływów pracy | Szum i zmęczenie kanału |
| Discord | Zespoły żyjące w Discordzie, lżejsze pętle sterowania | Mniej struktury przepływów pracy enterprise |
Używaj Slacka, gdy chcesz, aby rozmowa i przepływ pracy były interfejsem. Używaj narzędzi do powiadamiania, gdy alert nie jest opcjonalny. Jeśli баланsujesz projektowanie interakcji Slacka z granicami usług i wyborami trwałości, ten przegląd architektury aplikacji pomaga umiejscowić tę decyzję w większym systemie. Dla głębszego modelu routingu zobacz Współczesny projekt systemów alertowania dla zespołów obserwowalności. Dla alternatywy integracji Discorda zobacz Wzór integracji Discorda dla alertów i pętli sterowania.
Uwagi dotyczące dostępności i UX
- Wprowadzaj alerty o dużym wolumenie do własnego kanału i utrzymuj dyskusje ludzkie w wątkach.
- Używaj wątków dla kontekstu i aktualizacji na incydent. response_url może publikować na kanału i w wątkach, gdy podany jest thread_ts (Obsługa interakcji użytkownika).
- Używaj odpowiedzi efemerycznych przy potwierdzaniu akcji użytkownika, aby uniknąć spamu na kanale, ale pamiętaj, że dostarczanie efemeryczne nie jest gwarantowane i zależy od sesji (chat.postEphemeral).
- Używaj Block Kit Builder do szybkiego prototypowania układów (Block Kit).
- Jeśli dodajesz obrazy, dołącz znaczący tekst alternatywny, gdzie jest obsługiwany, i zachowaj zapasowy tekst zwykły w polu tekstowym najwyższego poziomu.
Kontrola bezpieczeństwa
- Weryfikuj każde przychodzące żądanie Slacka używając nagłówków sekretu podpisu i surowego ciała (Weryfikacja żądań z Slacka).
- Odrzucaj żądania ze znacznikami czasu starszymi niż pięć minut, aby zmniejszyć ryzyko odtworzenia (Weryfikacja żądań z Slacka).
- Przechowuj tokeny i URL webhooks w menedżerze tajemnic, nigdy w git.
- Używaj zakresów OAuth o najmniejszych uprawnieniach i rotuj sekrety, gdy ludzie zmieniają role (Odniesienie zakresów).
- Autoryzuj i uwierzytelnij wewnętrzne API akcji oddzielnie, nie traktuj Slacka jako granicy uwierzytelniania.
- Spraw, aby zatwierdzenia były idempotentne i deduplikowane.
- Loguj zatwierdzenia w sposób przyjazny dla audytu, w tym zespół, kanał, znacznik czasu wiadomości, użytkownika i akcję.
Podsumowanie
Slack jest w swojej najlepszej formie, gdy traktujesz go jako granicę systemową, a nie zbiornik wiadomości. Przychodzące webhooke pokrywają szybką dostawę alertów. Aplikacje plus interaktywność zamieniają Slacka w silnik przepływów pracy i interfejs zdarzeń. Trudne części to weryfikacja podpisu, ograniczenia czasowe, deduplikacja i wybór, gdzie Slack pasuje w Twoim modelu routingu alertów.
Następne linki: