Modelli di integrazione Slack per avvisi e flussi di lavoro
Slack è un'interfaccia utente per il flusso di lavoro e un livello di consegna degli alert.
Le integrazioni Slack sembrano ingannevolmente semplici perché è possibile pubblicare un messaggio in una sola chiamata HTTP. La parte interessante inizia quando si desidera che Slack sia interattivo e affidabile.

Questa analisi approfondita tratta Slack come tre diverse superfici di integrazione:
- Sink di notifica per alert unidirezionali tramite webhook in entrata.
- Motore di flussi di lavoro tramite Workflow Builder e passaggi personalizzati.
- Interfaccia di eventi tramite pulsanti Block Kit, comandi slash e payload di azione.
Questa pagina descrive come i sistemi attraversano il confine verso un’interfaccia utente condivisa che può anche emettere eventi indietro verso la tua architettura, non sulla filosofia degli alert. Per la strategia e il routing degli alert, consulta Progettazione dei Sistemi di Alert Moderni per i Team di Osservabilità.
Lettura correlata:
- Piattaforme di Chat come Interfacce di Sistema nei Sistemi Moderni
- Pattern di Integrazione Discord per Alert e Cicli di Controllo
- Progettazione dei Sistemi di Alert Moderni per i Team di Osservabilità
Inquadramento canonico e posizionamento nei pattern di integrazione
Slack non è solo il luogo dove gli alert vanno a morire. Se usato bene, Slack diventa un’interfaccia di sistema in cui i messaggi sono artefatti con stato e le interazioni degli utenti sono eventi.
Questa pagina è posizionata canonicamente sotto /app-architecture/integration-patterns/slack/ perché la domanda principale non è “dobbiamo inviare alert” ma “qual è il contratto tra il nostro sistema e Slack”.
Se la tua soluzione richiede uno dei seguenti, sei nel territorio dei pattern di integrazione, non in quello delle semplici notifiche:
- Un ciclo decisionale, dove un’approvazione umana abilita un’azione.
- Un flusso di lavoro, dove Slack raccoglie il contesto e attiva passaggi.
- Un ciclo di eventi, dove Slack emette azioni a cui il tuo sistema si abbona.
La piattaforma Slack supporta intenzionalmente sia la messaggistica unidirezionale che l’interazione bidirezionale tramite URL di richiesta e payload di interazione. I webhook in entrata sono un metodo di primo piano per inviare payload JSON, inclusi i componenti Block Kit, a un canale (Invio di messaggi utilizzando webhook in entrata). L’interattività viene restituita alla tua app come richieste HTTP POST a un URL di richiesta configurato, e quei payload sono codificati come form con un campo payload JSON (Gestione delle interazioni utente).
Slack come sink di notifica
I webhook in entrata sono la via più rapida per ottenere valore per gli alert e gli aggiornamenti di stato. Un webhook è un URL univoco legato a un’installazione dell’app, e vi invii un messaggio JSON (Invio di messaggi utilizzando webhook in entrata).
Opinione personale: i webhook sono un predefinito eccellente quando desideri messaggi “invia e dimentica” e non hai bisogno che Slack sia una superficie di controllo. I webhook sono anche un ottimo modo per disaccoppiare il tuo onboarding dalla tua architettura dell’app finale.
Slack come motore di flussi di lavoro
Workflow Builder esiste perché la chat è dove il lavoro avviene realmente. I flussi di lavoro possono essere semplici o complessi e possono connettersi alle app (Guida a Workflow Builder).
I passaggi personalizzati dei flussi di lavoro ti permettono di esporre i tuoi sistemi come blocchi componibili riutilizzabili all’interno di Workflow Builder (Passaggi dei flussi di lavoro). Questa è una forma di integrazione diversa rispetto ai bot nei canali. Sposta la tua integrazione più vicino al “strumentario all’interno di Slack” che a “messaggi dall’esterno”.
Opinione personale: se la tua organizzazione pensa già in termini di flussi di lavoro e approvazioni, i passaggi dei flussi di lavoro possono sembrare più nativi rispetto ai bot su misura.
Slack come interfaccia di eventi
Block Kit trasforma un messaggio in una superficie UI (Block Kit). I componenti interattivi come i pulsanti generano payload di azione, tipicamente payload block_actions, che vengono inviati alla tua app quando un utente fa clic (payload block_actions).
I pulsanti hanno identificatori espliciti action_id e un valore opzionale, e devono essere ospitati all’interno di blocchi section o actions (Elemento pulsante). Quando progetti un messaggio con un pulsante, stai progettando una fonte di eventi.
Qui è dove gli argomenti FAQ come la verifica delle richieste, gli ambiti necessari e i trigger interni sicuri diventano il centro del design.
Pattern di architettura scalabili
Flusso webhook per alert unidirezionali
[servizio] -> [formatore alert] -> [webhook in entrata Slack] -> [canale]
I webhook in entrata accettano payload JSON e supportano layout Block Kit (Invio di messaggi utilizzando webhook in entrata).
Quando l’FAQ chiede qual è il modo più veloce per inviare alert, solitamente è questo.
Flusso tramite broker con una coda per affidabilità e backpressure
[servizi] -> [argomento coda] -> [dispatcher Slack] -> [API Slack]
| |
| +-> [gestore limiti di frequenza]
+-> [coda dei messaggi morti]
I limiti di frequenza di Slack si applicano alle API basate su HTTP, inclusi i webhook in entrata, e Slack restituisce HTTP 429 con un header Retry-After quando superi i limiti (Limiti di frequenza).
Opinione personale: se invii alert direttamente da ogni servizio, il primo incidente si trasforma in un attacco di denial of service distribuito contro la tua stessa integrazione Slack. Un dispatcher dietro una coda tende a essere un’architettura più calma.
Pattern di automazione del flusso di lavoro con approvazioni
[alert] -> [messaggio Slack con pulsante] -> [clic sul pulsante]
-> [payload di azione] -> [gestore approvazioni] -> [API interna] -> [aggiorna messaggio]
L’interattività di Slack richiede la configurazione di un URL di Richiesta e l’abilitazione dell’Interattività. Slack invia payload di interazione come application/x-www-form-urlencoded con un parametro payload che contiene JSON, e devi rispondere con HTTP 200 entro 3 secondi (Gestione delle interazioni utente).
Questo è il pattern dietro l’articolo FAQ sull’attivazione sicura di azioni interne.
Diagramma del flusso di interazione Slack

Webhook vs app e la meccanica di implementazione
Librerie consigliate
Go:
- slack-go/slack per le strutture Web API e Block Kit (repo slack-go/slack, documentazione pkg.go.dev)
Python:
- slack_sdk (Python Slack SDK) per i client Web API, helper di firma e infrastruttura di ritentativo (documentazione Python Slack SDK, repo python-slack-sdk)
Approccio Webhook vs App Bot
Un confronto pratico:
| Capacità | Webhook in entrata | App Slack con token bot |
|---|---|---|
| Pubblicare messaggi | Sì | Sì |
| Pubblicare layout Block Kit | Sì | Sì |
| Ricevere clic sui pulsanti | Solo se legato a un’app con interattività | Sì |
| Comandi Slash | No | Sì |
| Passaggi del flusso di lavoro | No | Sì |
| Superficie di sicurezza | Segretezza dell’URL del webhook | Token OAuth più segreto di firma |
| Miglior adattamento | Alert unidirezionali | Flussi di lavoro, approvazioni, UI interattiva |
Slack supporta esplicitamente i layout Block Kit con i webhook in entrata (Invio di messaggi utilizzando webhook in entrata). L’interattività è configurata per app e consegnata a un URL di Richiesta (Gestione delle interazioni utente).
Opinione personale: i webhook sono un ottimo primo traguardo, ma non appena desideri che Slack sia una superficie di controllo, stai costruendo un’app. Evita di fingere il contrario.
Ambiti e permessi
Gli ambiti Slack definiscono cosa la tua app può fare. Esiste un riferimento centrale sugli ambiti e pagine individuali per ogni ambito (Riferimento sugli ambiti). Per inviare messaggi tramite Web API, chat:write è l’ambito canonico (ambito chat:write).
Per i comandi slash, hai tipicamente bisogno dell’ambito commands e di un URL di richiesta comando configurato (i comandi fanno parte della documentazione sull’interattività e ogni comando ha il proprio URL di Richiesta) (Gestione delle interazioni utente).
Nota FAQ: la consegna del payload del pulsante non è “un ambito”, è un’impostazione dell’app. La tua app riceve i payload quando l’Interattività è abilitata e l’URL di Richiesta è impostato, ma la pubblicazione di aggiornamenti del messaggio richiede generalmente ancora chat:write.
Limiti di frequenza e ritentativi
I limiti di frequenza di Slack restituiscono HTTP 429 e includono Retry-After in secondi, e ciò si applica alle API basate su HTTP inclusi i webhook in entrata (Limiti di frequenza).
In pratica:
- rispetta Retry-After
- applica backoff con jitter per errori transitori 5xx
- centralizza la consegna Slack in un dispatcher quando il volume cresce
Idempotenza e deduplicazione
Slack si aspetta un’accettazione per i payload di interazione entro 3 secondi, altrimenti gli utenti vedono un errore e Slack potrebbe ritentare la consegna in base alla funzionalità (Gestione delle interazioni utente). Per la Events API, Slack fornisce esplicitamente header di metadati di ritentativo x-slack-retry-num (API Eventi).
Anche senza ritentativi espliciti, si verificano duplicati perché gli utenti fanno doppio clic e perché i sistemi distribuiti ritrasmettono. Se il tuo pulsante attiva un’azione interna, tratta i clic come eventi “almeno una volta” e deduplica.
Una strategia pratica di idempotenza per le approvazioni:
- chiave idempotenza = team_id + channel_id + message_ts + action_id + user_id
- memorizza la chiave in Redis con TTL corrispondente alla finestra del tuo flusso di lavoro
- l’API di azione interna applica anche l’idempotenza, non solo il gestore Slack
Fondamenti di sicurezza e verifica delle richieste
Slack firma le richieste al tuo server utilizzando il tuo segreto di firma dell’app. Slack invia gli header X-Slack-Signature e X-Slack-Request-Timestamp, e Slack consiglia di rifiutare le richieste più vecchie di cinque minuti per prevenire attacchi di replay (Verifica delle richieste da Slack).
Due insidie che appaiono nelle revisioni del codice reali:
- Devi calcolare la firma sul corpo della richiesta grezzo, prima dell’analisi JSON o del decodifica del form (Verifica delle richieste da Slack).
- Devi accettare i payload interattivi entro 3 secondi, quindi esegui lavori pesanti in modo asincrono e usa
response_urlper comunicare i risultati (Gestione delle interazioni utente).
Il Python Slack SDK include un’utilità per la verifica della firma delle richieste nel codice e nella documentazione (verificatore di firma python-slack-sdk).
Design del messaggio e dell’interazione
Template del messaggio di alert
Se desideri che Slack agisca come un’interfaccia di sistema, struttura i tuoi messaggi in modo che le decisioni siano evidenti. Un template di messaggio che funziona bene tra i team:
- titolo
- gravità
- contesto
- suggerimento di azione
- link
Un template minimo:
titolo: tasso di errore del checkout elevato gravità: warn contesto: service=checkout env=prod region=us-east suggerimento di azione: clicca Approva riavvio per attivare un riavvio sicuro
Esempio di payload del webhook in entrata
I webhook in entrata accettano payload JSON e possono includere layout ricchi utilizzando Block Kit (Invio di messaggi utilizzando webhook in entrata).
{
"text": "tasso di errore del checkout elevato",
"blocks": [
{
"type": "header",
"text": { "type": "plain_text", "text": "tasso di errore del checkout elevato" }
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*gravità*\\nwarn" },
{ "type": "mrkdwn", "text": "*contesto*\\nservice=checkout env=prod region=us-east" }
]
},
{
"type": "section",
"text": { "type": "mrkdwn", "text": "*suggerimento di azione*\\nClicca Approva per attivare un riavvio sicuro." }
}
]
}
Progettazione di pulsanti e identificatori
I pulsanti devono essere all’interno di blocchi section o actions e includere action_id e un valore opzionale (Elemento pulsante). action_id è la tua chiave di routing. value è il tuo payload. Insieme, sono il tuo schema di eventi.
Opinione personale: scegli valori action_id come endpoint API stabili. Nomi come “approve_restart” invecchiano meglio di “button_1”.
Gestione del payload di interazione, response_url e tempistica
Slack invia i payload di interazione al tuo URL di Richiesta come dati codificati nel form con un parametro payload contenente JSON. Il payload include un campo type che definisce la fonte, come block_actions per i clic sui pulsanti (Gestione delle interazioni utente, Payload di interazione).
Devi restituire HTTP 200 entro 3 secondi per la risposta di accettazione (Gestione delle interazioni utente). Usa response_url per aggiornare il messaggio originale o rispondere nel canale o in un thread, e Slack limita l’uso di response_url a fino a cinque volte entro trenta minuti (Gestione delle interazioni utente).
Questo vincolo di tempistica è un vincolo di design. Ti costringe a disaccoppiare “accetta” da “esegui il lavoro”.
Pattern di interazione adatti a Slack
- Pulsanti in Block Kit per approvazioni e ramificazioni.
- Comandi slash per intento utente esplicito e parametri.
- Passaggi del flusso di lavoro per processi aziendali ripetibili in Workflow Builder (Passaggi del flusso di lavoro).
- Scorciatoie e modali quando hai bisogno di input strutturato, con vincoli
trigger_iddescritti nella documentazione sull’interattività (Gestione delle interazioni utente).
Esempi in Go e Python
Nota dell’autore: questi possono essere suddivisi in pagine di esempio dedicate:
/app-architecture/integration-patterns/slack/go-example/app-architecture/integration-patterns/slack/python-example
Gli esempi danno priorità a una cosa che mantiene i sistemi stabili:
- verifica le firme Slack
- accetta entro tre secondi
- deduplica le azioni
- attiva un HTTP POST interno
- opzionalmente aggiorna Slack usando
response_url
Esempio Go: invia alert e gestisci approvazione pulsante
Prerequisiti:
- App Slack con Interattività abilitata e URL di Richiesta configurato (Gestione delle interazioni utente).
- Token bot con ambito
chat:write(ambitochat:write). - Un segreto di firma per la verifica delle richieste (Verifica delle richieste da Slack).
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 (
// In produzione, memorizza questo in Redis con 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") // es. :8080
if botToken == "" || signingSecret == "" || channelID == "" || internalURL == "" || listenAddr == "" {
log.Fatal("variabili d'ambiente mancanti SLACK_BOT_TOKEN SLACK_SIGNING_SECRET SLACK_CHANNEL_ID INTERNAL_API_URL LISTEN_ADDR")
}
api := slack.New(botToken)
// Invia un messaggio di alert con un pulsante di approvazione.
// I pulsanti sono elementi interattivi Block Kit con action_id e value.
// Vedi documentazione elemento pulsante Block Kit di Slack.
blocks := slack.Blocks{
BlockSet: []slack.Block{
slack.NewHeaderBlock(slack.NewTextBlockObject("plain_text", "tasso di errore del checkout elevato", false, false)),
slack.NewSectionBlock(
slack.NewTextBlockObject("mrkdwn", "*gravità*\\nwarn\\n*contesto*\\nservice=checkout env=prod", false, false),
nil,
nil,
),
slack.NewActionBlock(
"actions_1",
slack.NewButtonBlockElement("approve_restart", "restart", slack.NewTextBlockObject("plain_text", "Approva riavvio", false, false)),
),
},
}
_, ts, err := api.PostMessage(channelID, slack.MsgOptionBlocks(blocks.BlockSet...))
if err != nil {
log.Fatalf("PostMessage fallito: %v", err)
}
log.Printf("messaggio alert inviato message_ts=%s", ts)
// Endpoint di interattività
http.HandleFunc("/slack/actions", func(w http.ResponseWriter, r *http.Request) {
rawBody, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "lettura corpo fallita", http.StatusBadRequest)
return
}
r.Body.Close()
// Verifica la firma della richiesta Slack sul corpo grezzo e il timestamp.
// Vedi documentazione Slack sulla verifica delle richieste.
if !verifySlackRequest(r.Header, rawBody, signingSecret) {
http.Error(w, "firma non valida", http.StatusUnauthorized)
return
}
// Slack invia application/x-www-form-urlencoded con payload=JSON
vals, err := url.ParseQuery(string(rawBody))
if err != nil {
http.Error(w, "corpo form non valido", http.StatusBadRequest)
return
}
payloadStr := vals.Get("payload")
if payloadStr == "" {
http.Error(w, "payload mancante", http.StatusBadRequest)
return
}
var p BlockActionPayload
if err := json.Unmarshal([]byte(payloadStr), &p); err != nil {
http.Error(w, "json payload non valido", http.StatusBadRequest)
return
}
// Accetta entro 3 secondi. Esegui il lavoro reale in modo asincrono e usa response_url per gli aggiornamenti.
// Vedi documentazione Slack sulla tempistica dell'accettazione.
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
}
// Deduplica le approvazioni
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("azione interna fallita: %v", err)
_ = replyViaResponseURL(p.ResponseURL, "azione fallita, controlla i log")
return
}
_ = replyViaResponseURL(p.ResponseURL, "approvazione ricevuta, azione interna attivata")
}()
})
log.Printf("ascolto su %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
}
// Rifiuta richieste più vecchie di 5 minuti per ridurre il rischio di replay.
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 accetta payload JSON e può pubblicare come ephemeral di default.
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
}
Esempio Python: invia alert e gestisci approvazione pulsante
Prerequisiti:
- App Slack con Interattività abilitata e URL di Richiesta configurato (Gestione delle interazioni utente).
- Token bot con ambito
chat:write(ambitochat:write). - Segreto di firma per la verifica delle richieste (Verifica delle richieste da Slack).
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__)
# In produzione, memorizza questi in 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": "tasso di errore del checkout elevato"}},
{"type": "section", "text": {"type": "mrkdwn", "text": "*gravità*\\nwarn\\n*contesto*\\nservice=checkout env=prod"}},
{
"type": "actions",
"block_id": "actions_1",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "Approva riavvio"},
"action_id": "approve_restart",
"value": "restart"
}
]
}
]
# chat.postMessage richiede ambito chat:write.
client.chat_postMessage(channel=SLACK_CHANNEL_ID, text="tasso di errore del checkout elevato", blocks=blocks)
@app.post("/slack/actions")
def slack_actions():
raw_body = request.get_data() # Slack consiglia di verificare il corpo grezzo prima dell'analisi
if not verifier.is_valid_request(raw_body, request.headers):
return make_response("firma non valida", 401)
# Slack invia application/x-www-form-urlencoded con un campo payload contenente JSON.
payload_str = request.form.get("payload", "")
if not payload_str:
return make_response("payload mancante", 400)
payload = json.loads(payload_str)
# Accetta entro 3 secondi. Slack mostra errori agli utenti se non lo fai.
# Vedi documentazione Slack sull'accettazione.
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", ""), "approvazione ricevuta, azione interna attivata")
except Exception:
reply_via_response_url(payload.get("response_url", ""), "azione fallita, controlla i log")
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")))
Note operative: routing, UX, sicurezza, link e SEO
Quando usare Slack vs strumenti di paging vs Discord
Questa pagina riguarda la meccanica. Il routing è strategia. Tuttavia, il confine è facile da descrivere.
| Canale | Migliore per | Modalità di fallimento |
|---|---|---|
| PagerDuty o equivalente | Impatto utente urgente che richiede risposta immediata | Le persone dormono attraverso Slack |
| Slack | Coordinamento, approvazioni, esecuzione del flusso di lavoro | Rumore e affaticamento del canale |
| Discord | Team che vivono su Discord, cicli di controllo più leggeri | Meno struttura dei flussi di lavoro enterprise |
Usa Slack quando vuoi che la conversazione e il flusso di lavoro siano l’interfaccia. Usa gli strumenti di paging quando l’alert non è opzionale. Se stai bilanciando il design dell’interazione Slack contro i confini del servizio e le scelte di persistenza, questa panoramica sull’architettura delle app aiuta a inserire quella decisione nel sistema più ampio. Per un modello di routing più approfondito, vedi Progettazione dei Sistemi di Alert Moderni per i Team di Osservabilità. Per un’alternativa di integrazione Discord, vedi Pattern di Integrazione Discord per Alert e Cicli di Controllo.
Note sull’accessibilità e l’esperienza utente (UX)
- Inserisci gli alert ad alto volume nel loro canale dedicato e mantieni le discussioni umane nei thread.
- Usa i thread per il contesto e gli aggiornamenti per incidente.
response_urlpuò pubblicare nel canale e nei thread quando viene fornitothread_ts(Gestione delle interazioni utente). - Usa risposte effimere quando accetti le azioni dell’utente per evitare spam nel canale, ma ricorda che la consegna effimera non è garantita ed è dipendente dalla sessione (chat.postEphemeral).
- Usa Block Kit Builder per prototipare layout rapidamente (Block Kit).
- Se aggiungi immagini, includi testo alternativo significativo dove supportato e mantieni un fallback in testo semplice nel campo
textdi livello superiore.
Checklist di sicurezza
- Verifica ogni richiesta Slack in entrata utilizzando gli header del segreto di firma e il corpo grezzo (Verifica delle richieste da Slack).
- Rifiuta le richieste con timestamp più vecchi di cinque minuti per ridurre il rischio di replay (Verifica delle richieste da Slack).
- Mantieni token e URL dei webhook in un gestore di segreti, mai in git.
- Usa ambiti OAuth con privilegi minimi e ruota i segreti quando le persone cambiano ruolo (Riferimento sugli ambiti).
- Autentica e autorizza l’API di azione interna separatamente, non trattare Slack come un confine di autenticazione.
- Rendi le approvazioni idempotenti e deduplicate.
- Registra le approvazioni in modo amichevole per l’audit, includendo team, canale, timestamp del messaggio, utente e azione.
Conclusione
Slack è al suo meglio quando lo tratti come un confine di sistema, non come un sink di messaggi. I webhook in entrata coprono la consegna rapida degli alert. Le App più l’interattività trasformano Slack in un motore di flussi di lavoro e un’interfaccia di eventi. Le parti difficili sono la verifica della firma, i vincoli di tempistica, la deduplicazione e la scelta di dove Slack si inserisce nel tuo modello di routing degli alert.
Link successivi: