Шаблоны интеграции Slack для оповещений и рабочих процессов
Slack — это слой интерфейса пользовательского взаимодействия для рабочих процессов и доставки уведомлений.
Интеграции со Slack могут показаться обманчиво простыми, потому что вы можете отправить сообщение одним HTTP-запросом. Интересная часть начинается тогда, когда вы хотите, чтобы Slack был интерактивным и надежным.

В этом подробном обзоре Slack рассматривается как три различных поверхности интеграции:
- Приемник уведомлений для односторонних предупреждений через входящие веб-хуки.
- Движок рабочих процессов через Workflow Builder и пользовательские шаги рабочих процессов.
- Интерфейс событий через кнопки Block Kit, слэш-команды и полезные нагрузки действий.
Эта страница описывает, как системы пересекают границу в общий интерфейс пользователя, который также может генерировать события обратно в вашу архитектуру, а не о философии оповещений. О стратегии и маршрутизации оповещений см. Проектирование современных систем оповещения для команд наблюдаемости.
Сопутствующие материалы:
- Платформы чата как системные интерфейсы в современных системах
- Паттерн интеграции Discord для оповещений и циклов управления
- Проектирование современных систем оповещения для команд наблюдаемости
Каноническая рамка и размещение в паттернах интеграции
Slack — это не просто место, где оповещения умирают. При правильном использовании Slack становится системным интерфейсом, где сообщения являются артефактами состояния, а взаимодействия пользователей — событиями.
Эта страница канонически размещена под /app-architecture/integration-patterns/slack/, потому что основной вопрос заключается не в «стоит ли оповещать», а в «какой контракт между нашей системой и Slack».
Если ваше решение требует любого из следующего, вы находитесь в области паттернов интеграции, а не простого оповещения:
- Цикл принятия решений, где действие блокируется до утверждения человеком.
- Рабочий процесс, где Slack собирает контекст и запускает шаги.
- Цикл событий, где Slack генерирует действия, на которые ваша система подписывается.
Платформа Slack намеренно поддерживает как одностороннюю отправку сообщений, так и двустороннее взаимодействие через URL запросов и полезные нагрузки взаимодействий. Входящие веб-хуки являются основным способом отправки JSON-полезных нагрузок, включая строительные блоки Block Kit, в канал (Отправка сообщений с использованием входящих веб-хуков). Интерактивность возвращается вашему приложению в виде HTTP POST-запросов к настроенному URL запроса, и эти полезные нагрузки кодируются в формате формы с полем JSON (Обработка взаимодействия пользователя).
Slack как приемник уведомлений
Входящие веб-хуки — это самый быстрый путь к ценности для оповещений и обновлений статуса. Веб-хук — это уникальный URL, привязанный к установке приложения, и вы отправляете к нему JSON-сообщение (Отправка сообщений с использованием входящих веб-хуков).
Субъективное мнение: веб-хуки — отличный выбор по умолчанию, когда вам нужны сообщения «отправить и забыть», и вам не нужен Slack как поверхность управления. Веб-хуки также являются отличным способом разделить настраивание и вашу конечную архитектуру приложения.
Slack как движок рабочих процессов
Workflow Builder существует потому, что чат — это там, где работа действительно происходит. Рабочие процессы могут быть простыми или сложными и могут подключаться к приложениям (Руководство по Workflow Builder).
Пользовательские шаги рабочих процессов позволяют представить ваши системы как переиспользуемые строительные блоки внутри Workflow Builder (Шаги рабочих процессов). Это другая форма интеграции, чем боты в каналах. Это приближает вашу интеграцию к «инструментам внутри Slack», а не к «сообщениям извне».
Субъективное мнение: если ваша организация уже мыслит категориями рабочих процессов и согласований, шаги рабочих процессов могут казаться более нативными, чем специализированные боты.
Slack как интерфейс событий
Block Kit превращает сообщение в поверхность интерфейса пользователя (Block Kit). Интерактивные компоненты, такие как кнопки, генерируют полезные нагрузки действий, обычно полезные нагрузки block_actions, которые отправляются в ваше приложение, когда пользователь нажимает (полезная нагрузка block_actions).
Кнопки имеют явные идентификаторы action_id и необязательное значение, и должны находиться внутри блоков section или actions (Элемент кнопки). Когда вы проектируете сообщение с кнопкой, вы проектируете источник событий.
Здесь темы, подобные проверке запросов, необходимым правам доступа и безопасным внутренним триггерам, становятся центром проектирования.
Архитектурные паттерны, которые масштабируются
Поток веб-хука для односторонних оповещений
[сервис] -> [форматировщик оповещений] -> [входящий веб-хук Slack] -> [канал]
Входящие веб-хуки принимают JSON-полезные нагрузки и поддерживают макеты Block Kit (Отправка сообщений с использованием входящих веб-хуков).
Когда в FAQ спрашивают о самом быстром способе отправки оповещений, обычно это именно оно.
Поток через брокер с очередью для надежности и обратного давления
[сервисы] -> [тема очереди] -> [диспетчер Slack] -> [API Slack]
| |
| +-> [обработчик ограничения частоты]
+-> [очередь мертвых писем]
Ограничения частоты запросов Slack применяются к HTTP-базовым API, включая входящие веб-хуки, и Slack возвращает HTTP 429 с заголовком Retry-After, когда вы превышаете лимиты (Ограничения частоты).
Субъективное мнение: если вы отправляете оповещения напрямую от каждого сервиса, первый инцидент превращается в DDoS-атаку против вашей собственной интеграции со Slack. Диспетчер за очередью, как правило, создает более спокойную архитектуру.
Паттерн автоматизации рабочих процессов с согласованиями
[оповещение] -> [сообщение Slack с кнопкой] -> [нажатие кнопки]
-> [полезная нагрузка действия] -> [обработчик согласования] -> [внутренний API] -> [обновление сообщения]
Интерактивность Slack требует настройки URL запроса и включения интерактивности. Slack отправляет полезные нагрузки взаимодействий как application/x-www-form-urlencoded с параметром payload, содержащим JSON, и вы должны ответить HTTP 200 в течение 3 секунд (Обработка взаимодействия пользователя).
Это паттерн, лежащий в основе вопроса FAQ о безопасном запуске внутренних действий.
Диаграмма потока взаимодействия со Slack

Веб-хук против приложения и механика реализации
Рекомендуемые библиотеки
Go:
- slack-go/slack для структур Web API и Block Kit (репозиторий slack-go/slack, документация pkg.go.dev)
Python:
- slack_sdk (Python Slack SDK) для клиентов Web API, помощников по подписи и инфраструктуры повторных попыток (документация Python Slack SDK, репозиторий python-slack-sdk)
Подход с веб-хуком против бота приложения
Практическое сравнение:
| Возможность | Входящий веб-хук | Приложение Slack с токеном бота |
|---|---|---|
| Отправка сообщений | Да | Да |
| Отправка макетов Block Kit | Да | Да |
| Получение нажатий кнопок | Только если привязано к приложению с интерактивностью | Да |
| Слэш-команды | Нет | Да |
| Шаги рабочих процессов | Нет | Да |
| Поверхность безопасности | Тайна URL веб-хука | Токены OAuth плюс секрет подписи |
| Лучшее применение | Односторонние оповещения | Рабочие процессы, согласования, интерактивный интерфейс |
Slack явно поддерживает макеты Block Kit с входящими веб-хуками (Отправка сообщений с использованием входящих веб-хуков). Интерактивность настраивается для каждого приложения и доставляется на URL запроса (Обработка взаимодействия пользователя).
Субъективное мнение: веб-хуки — это отличная первая веха, но как только вы захотите, чтобы Slack стал поверхностью управления, вы строите приложение. Не стоит притворяться обратным.
Области доступа и разрешения
Области доступа Slack определяют, что может делать ваше приложение. Есть центральная справочная страница по областям доступа и отдельные страницы для каждой области (Справочник областей доступа). Для отправки сообщений через Web API канонической областью доступа является chat:write (область доступа chat:write).
Для слэш-команд обычно требуется область доступа commands и настроенный URL запроса команды (команды являются частью документации по интерактивности, и у каждой команды есть свой URL запроса) (Обработка взаимодействия пользователя).
Примечание FAQ: доставка полезной нагрузки кнопок — это «не область доступа», это настройка приложения. Ваше приложение получает полезные нагрузки, когда включена интерактивность и установлен URL запроса, но для обновления сообщений все еще обычно требуется chat:write.
Ограничения частоты и повторные попытки
Ограничения частоты запросов Slack возвращают HTTP 429 и включают Retry-After в секундах, и это применяется к HTTP-базовым API, включая входящие веб-хуки (Ограничения частоты).
На практике:
- соблюдайте Retry-After
- применяйте экспоненциальную задержку с дрожанием для временных ошибок 5xx
- централизуйте доставку Slack в диспетчере, когда объем растет
Идемпотентность и дедупликация
Slack ожидает подтверждения для полезных нагрузок взаимодействия в течение 3 секунд, иначе пользователи видят ошибку, и Slack может повторить доставку в зависимости от функции (Обработка взаимодействия пользователя). Для API событий Slack явно предоставляет заголовки метаданных повторных попыток x-slack-retry-num (API событий).
Даже без явных повторных попыток дубликаты возникают, потому что пользователи дважды кликают и потому что распределенные системы повторяют передачу. Если ваша кнопка запускает внутреннее действие, относитесь к кликам как к событиям «хотя бы один раз» и дедуплицируйте.
Практическая стратегия идемпотентности для согласований:
- ключ идемпотентности = team_id + channel_id + message_ts + action_id + user_id
- храните ключ в Redis с TTL, соответствующим вашему временному окну рабочего процесса
- внутренний API действий также обеспечивает идемпотентность, а не только обработчик Slack
Основы безопасности и проверка запросов
Slack подписывает запросы к вашему серверу, используя секрет подписи вашего приложения. Slack отправляет заголовки X-Slack-Signature и X-Slack-Request-Timestamp, и Slack рекомендует отклонять запросы старше пяти минут для предотвращения атак повторного воспроизведения (Проверка запросов от Slack).
Две ловушки, которые появляются в реальных обзорах кода:
- Вы должны вычислить подпись над сырым телом запроса, до разбора JSON или декодирования формы (Проверка запросов от Slack).
- Вы должны подтверждать интерактивные полезные нагрузки в течение 3 секунд, поэтому выполняйте тяжелую работу асинхронно и используйте response_url для передачи результатов (Обработка взаимодействия пользователя).
Python Slack SDK включает утилиту верификатора подписи запроса в коде и документации (верификатор подписи python-slack-sdk).
Проектирование сообщений и взаимодействий
Шаблон сообщения об оповещении
Если вы хотите, чтобы Slack действовал как системный интерфейс, структурируйте свои сообщения так, чтобы решения были очевидны. Шаблон сообщения, который хорошо работает в разных командах:
- заголовок
- серьезность
- контекст
- подсказка действия
- ссылки
Минимальный шаблон:
заголовок: повышен уровень ошибок при оформлении заказа серьезность: warn контекст: service=checkout env=prod region=us-east подсказка действия: нажмите Approve restart для запуска безопасной перезагрузки
Пример полезной нагрузки входящего веб-хука
Входящие веб-хуки принимают JSON-полезные нагрузки и могут включать богатые макеты с использованием Block Kit (Отправка сообщений с использованием входящих веб-хуков).
{
"text": "checkout error rate elevated",
"blocks": [
{
"type": "header",
"text": { "type": "plain_text", "text": "checkout error rate elevated" }
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*severity*\\nwarn" },
{ "type": "mrkdwn", "text": "*context*\\nservice=checkout env=prod region=us-east" }
]
},
{
"type": "section",
"text": { "type": "mrkdwn", "text": "*action_hint*\\nClick Approve to trigger a safe restart." }
}
]
}
Проектирование кнопок и идентификаторов
Кнопки должны находиться внутри блоков section или actions и включать action_id и необязательное значение (Элемент кнопки). action_id — это ваш ключ маршрутизации. value — это ваша полезная нагрузка. Вместе они являются вашей схемой событий.
Субъективное мнение: выбирайте значения action_id как стабильные конечные точки API. Названия вроде “approve_restart” стареют лучше, чем “button_1”.
Обработка полезной нагрузки взаимодействия, response_url и тайминг
Slack отправляет полезные нагрузки взаимодействий на ваш URL запроса как данные, закодированные в формате формы, с параметром payload, содержащим JSON. Полезная нагрузка включает поле type, определяющее источник, например block_actions для нажатий кнопок (Обработка взаимодействия пользователя, Полезные нагрузки взаимодействий).
Вы должны вернуть HTTP 200 в течение 3 секунд для ответа подтверждения (Обработка взаимодействия пользователя). Используйте response_url для обновления исходного сообщения или ответа в канале или в потоке, и Slack ограничивает использование response_url до пяти раз в течение тридцати минут (Обработка взаимодействия пользователя).
Это ограничение по времени является ограничением проектирования. Оно заставляет вас разделять «подтверждение» и «выполнение работы».
Паттерны взаимодействия, подходящие для Slack
- Кнопки в Block Kit для согласований и ветвления.
- Слэш-команды для явного намерения пользователя и параметров.
- Шаги рабочих процессов для повторяемых бизнес-процессов в Workflow Builder (Шаги рабочих процессов).
- Ярлыки и модальные окна, когда вам нужен структурированный ввод, с ограничениями trigger_id, описанными в документации по интерактивности (Обработка взаимодействия пользователя).
Примеры на Go и Python
Примечание издателя: их можно разделить на отдельные страницы с примерами:
/app-architecture/integration-patterns/slack/go-example/app-architecture/integration-patterns/slack/python-example
Примеры приоритезируют одну вещь, которая держит системы стабильными:
- проверка подписей Slack
- подтверждение в течение трех секунд
- дедупликация действий
- запуск внутреннего HTTP POST
- опциональное обновление Slack с использованием response_url
Пример на Go: отправка оповещения и обработка согласования кнопки
Предварительные требования:
- Приложение Slack с включенной интерактивностью и настроенным URL запроса (Обработка взаимодействия пользователя).
- Токен бота с областью доступа chat:write (область доступа chat:write).
- Секрет подписи для проверки запросов (Проверка запросов от 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 (
// В продакшене храните это в Redis с 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") // e.g. :8080
if botToken == "" || signingSecret == "" || channelID == "" || internalURL == "" || listenAddr == "" {
log.Fatal("missing env vars SLACK_BOT_TOKEN SLACK_SIGNING_SECRET SLACK_CHANNEL_ID INTERNAL_API_URL LISTEN_ADDR")
}
api := slack.New(botToken)
// Отправьте сообщение об оповещении с кнопкой согласования.
// Кнопки — это интерактивные элементы Block Kit с action_id и value.
// См. документацию Slack по элементам кнопок Block Kit.
blocks := slack.Blocks{
BlockSet: []slack.Block{
slack.NewHeaderBlock(slack.NewTextBlockObject("plain_text", "checkout error rate elevated", false, false)),
slack.NewSectionBlock(
slack.NewTextBlockObject("mrkdwn", "*severity*\\nwarn\\n*context*\\nservice=checkout env=prod", false, false),
nil,
nil,
),
slack.NewActionBlock(
"actions_1",
slack.NewButtonBlockElement("approve_restart", "restart", slack.NewTextBlockObject("plain_text", "Approve 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)
// Конечная точка интерактивности
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()
// Проверьте подпись запроса Slack на сыром теле и временной метке.
// См. документацию Slack по проверке запросов.
if !verifySlackRequest(r.Header, rawBody, signingSecret) {
http.Error(w, "invalid signature", http.StatusUnauthorized)
return
}
// Slack отправляет application/x-www-form-urlencoded с 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
}
// Подтвердите в течение 3 секунд. Выполняйте реальную работу асинхронно и используйте response_url для обновлений.
// См. документацию Slack по интерактивности о времени подтверждения.
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
}
// Дедупликация согласований
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
}
// Отклоните запросы старше 5 минут для снижения риска повторного воспроизведения.
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 принимает JSON-полезные нагрузки и может отправлять эфемерные сообщения по умолчанию.
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
}
Пример на Python: отправка оповещения и обработка согласования кнопки
Предварительные требования:
- Приложение Slack с включенной интерактивностью и настроенным URL запроса (Обработка взаимодействия пользователя).
- Токен бота с областью доступа chat:write (область доступа chat:write).
- Секрет подписи для проверки запросов (Проверка запросов от 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__)
# В продакшене храните это в 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": "checkout error rate elevated"}},
{"type": "section", "text": {"type": "mrkdwn", "text": "*severity*\\nwarn\\n*context*\\nservice=checkout env=prod"}},
{
"type": "actions",
"block_id": "actions_1",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "Approve restart"},
"action_id": "approve_restart",
"value": "restart"
}
]
}
]
# chat.postMessage требует области доступа chat:write.
client.chat_postMessage(channel=SLACK_CHANNEL_ID, text="checkout error rate elevated", blocks=blocks)
@app.post("/slack/actions")
def slack_actions():
raw_body = request.get_data() # Slack рекомендует проверять сырое тело до разбора
if not verifier.is_valid_request(raw_body, request.headers):
return make_response("invalid signature", 401)
# Slack отправляет application/x-www-form-urlencoded с полем payload, содержащим JSON.
payload_str = request.form.get("payload", "")
if not payload_str:
return make_response("missing payload", 400)
payload = json.loads(payload_str)
# Подтвердите в течение 3 секунд. Пользователь Slack увидит ошибки, если вы этого не сделаете.
# См. документацию Slack по интерактивности о подтверждении.
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")))
Примечания по эксплуатации: маршрутизация, UX, безопасность, ссылки и SEO
Когда использовать Slack, инструменты пейджинга или Discord
Эта страница посвящена механике. Маршрутизация — это стратегия. Тем не менее, границу легко описать.
| Канал | Лучше всего для | Режим сбоя |
|---|---|---|
| PagerDuty или аналог | Срочное влияние на пользователя, требующее немедленного реагирования | Люди спят во время Slack |
| Slack | Координация, согласования, выполнение рабочих процессов | Шум и усталость от каналов |
| Discord | Команды, живущие в Discord, более легкие циклы управления | Меньше структуры корпоративных рабочих процессов |
Используйте Slack, когда вы хотите, чтобы разговор и рабочий процесс были интерфейсом. Используйте инструменты пейджинга, когда оповещение не является опциональным. Если вы балансируете дизайн взаимодействия со Slack, границами сервисов и выбором постоянства, этот обзор архитектуры приложений поможет разместить это решение в более широкой системе. Для более глубокой модели маршрутизации см. Проектирование современных систем оповещения для команд наблюдаемости. Для альтернативы интеграции с Discord см. Паттерн интеграции Discord для оповещений и циклов управления.
Примечания по доступности и UX
- Помещайте высокообъемные оповещения в свой канал и сохраняйте обсуждения людей в потоках.
- Используйте потоки для контекста и обновлений по каждому инциденту. response_url может отправлять сообщения в канале и в потоках, если предоставлен thread_ts (Обработка взаимодействия пользователя).
- Используйте эфемерные ответы при подтверждении действий пользователя, чтобы избежать спама в канале, но помните, что доставка эфемерных сообщений не гарантирована и зависит от сессии (chat.postEphemeral).
- Используйте Block Kit Builder для быстрой прототипизации макетов (Block Kit).
- Если вы добавляете изображения, включайте осмысленный альт-текст там, где это поддерживается, и сохраняйте текстовый запасной вариант в поле верхнего уровня text.
Контрольный список безопасности
- Проверяйте каждый входящий запрос Slack, используя заголовки секрета подписи и сырое тело (Проверка запросов от Slack).
- Отклоняйте запросы с временными метками старше пяти минут для снижения риска повторного воспроизведения (Проверка запросов от Slack).
- Храните токены и URL веб-хуков в менеджере секретов, никогда в git.
- Используйте минимально необходимые OAuth-области доступа и меняйте секреты при смене ролей людей (Справочник областей доступа).
- Аутентифицируйте и авторизуйте внутренний API действий отдельно, не рассматривайте Slack как границу аутентификации.
- Делайте согласования идемпотентными и дедуплицированными.
- Вести журнал согласований удобным для аудита образом, включая команду, канал, временную метку сообщения, пользователя и действие.
Заключение
Slack показывает лучшие результаты, когда вы относитесь к нему как к системной границе, а не к приемнику сообщений. Входящие веб-хуки покрывают быструю доставку оповещений. Приложения плюс интерактивность превращают Slack в движок рабочих процессов и интерфейс событий. Сложные части — это проверка подписей, временные ограничения, дедупликация и выбор того, где Slack вписывается в вашу модель маршрутизации оповещений.
Следующие ссылки: