Как отправить Discord Webhook на Go (Golang)
Практическое руководство по отправке сообщений Discord webhook на Go через net/http. Текст, embed, файлы и обработка rate limit — без внешних библиотек.
🇬🇧 Доступно также на English
Go часто выбирают для бэкенд-сервисов, CLI-утилит и агентов мониторинга — именно того софта, которому полезно слать оповещения в Discord. Приятный момент: отправить Discord webhook на Go можно только средствами стандартной библиотеки, без внешних зависимостей.
В руководстве разберём обычный текст, embed-сообщения, загрузку файлов, кастомное имя и корректную обработку rate limit.
Что такое Discord Webhook?
Discord webhook — это URL, который публикует сообщения в канал при получении HTTP-запроса POST. В отличие от бота, ему не нужны постоянное соединение и обмен токенами — идеально для лёгкого сервиса на Go. Если URL ещё нет, следуйте гайду как получить URL Discord webhook.
Что понадобится
Нужен Go версии 1.16 или новее. Никакого go get — весь код ниже использует net/http, encoding/json и bytes из стандартной библиотеки.
Шаг 1: Отправка простого текста
package main
import (
"bytes"
"encoding/json"
"log"
"net/http"
)
const webhookURL = "https://discord.com/api/webhooks/ВАШ_URL_ВЕБХУКА"
type Payload struct {
Content string `json:"content,omitempty"`
}
func main() {
body, err := json.Marshal(Payload{Content: "Привет из Go! 🐹"})
if err != nil {
log.Fatal(err)
}
resp, err := http.Post(webhookURL, "application/json", bytes.NewReader(body))
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNoContent {
log.Println("Сообщение отправлено!")
} else {
log.Printf("Ошибка: %s", resp.Status)
}
}
Успешный запрос возвращает 204 No Content.
Шаг 2: Отправка embed-сообщения
Embed позволяет форматировать сообщение с заголовком, описанием, цветной полосой и полями. Опишите их структурами Go, чтобы JSON оставался типобезопасным:
type Embed struct {
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Color int `json:"color,omitempty"`
Fields []EmbedField `json:"fields,omitempty"`
Footer *EmbedFooter `json:"footer,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
}
type EmbedField struct {
Name string `json:"name"`
Value string `json:"value"`
Inline bool `json:"inline,omitempty"`
}
type EmbedFooter struct {
Text string `json:"text"`
}
type EmbedPayload struct {
Embeds []Embed `json:"embeds"`
}
Теперь соберём и отправим:
func sendEmbed() error {
embed := Embed{
Title: "Статус сервера",
Description: "Все системы работают штатно.",
Color: 5763719, // Зелёный в десятичном виде
Fields: []EmbedField{
{Name: "Загрузка CPU", Value: "23%", Inline: true},
{Name: "Память", Value: "4.2 ГБ / 16 ГБ", Inline: true},
{Name: "Аптайм", Value: "14 дней", Inline: false},
},
Footer: &EmbedFooter{Text: "Агент мониторинга"},
Timestamp: time.Now().Format(time.RFC3339),
}
body, _ := json.Marshal(EmbedPayload{Embeds: []Embed{embed}})
resp, err := http.Post(webhookURL, "application/json", bytes.NewReader(body))
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
Цвета Discord — это десятичные целые. Несколько частых: красный 15548997, зелёный 5763719, blurple 5793266, жёлтый 16776960.
Шаг 3: Своё имя и аватар
Добавьте эти поля в структуру payload, чтобы менять оформление каждого сообщения:
type Payload struct {
Content string `json:"content,omitempty"`
Username string `json:"username,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
}
body, _ := json.Marshal(Payload{
Content: "Деплой завершён! ✅",
Username: "Deploy Bot",
AvatarURL: "https://example.com/deploy-avatar.png",
})
Шаг 4: Загрузка файла
Чтобы прикрепить файл, отправьте multipart/form-data вместо JSON. Положите payload сообщения в поле payload_json рядом с файлом:
func sendFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
var buf bytes.Buffer
w := multipart.NewWriter(&buf)
// Часть с JSON-payload
payload, _ := json.Marshal(map[string]string{"content": "Вот файл лога:"})
w.WriteField("payload_json", string(payload))
// Часть с файлом
part, err := w.CreateFormFile("file", filepath.Base(path))
if err != nil {
return err
}
io.Copy(part, file)
w.Close()
req, _ := http.NewRequest("POST", webhookURL, &buf)
req.Header.Set("Content-Type", w.FormDataContentType())
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
Не забудьте импортировать bytes, mime/multipart, os, io и path/filepath. О лимитах размера и загрузке нескольких файлов — в отдельном гайде отправка файлов через вебхуки.
Шаг 5: Обработка rate limit
Discord ограничивает вебхуки примерно 5 запросами за 2 секунды. При превышении вы получите 429 Too Many Requests со значением retry_after. Учтите его в небольшом хелпере с ретраями:
func sendWithRetry(payload []byte, maxRetries int) error {
for attempt := 0; attempt < maxRetries; attempt++ {
resp, err := http.Post(webhookURL, "application/json", bytes.NewReader(payload))
if err != nil {
return err
}
if resp.StatusCode == http.StatusNoContent {
resp.Body.Close()
return nil
}
if resp.StatusCode == http.StatusTooManyRequests {
var r struct {
RetryAfter float64 `json:"retry_after"`
}
json.NewDecoder(resp.Body).Decode(&r)
resp.Body.Close()
time.Sleep(time.Duration(r.RetryAfter * float64(time.Second)))
continue
}
resp.Body.Close()
return fmt.Errorf("неожиданный статус: %s", resp.Status)
}
return fmt.Errorf("ретраи исчерпаны")
}
В руководстве по rate limits подробно разобраны бакеты по каналу и по вебхуку.
Безопасность: храните URL в переменной окружения
Никогда не зашивайте URL вебхука в коммитимый код. Читайте его из окружения:
webhookURL := os.Getenv("DISCORD_WEBHOOK_URL")
if webhookURL == "" {
log.Fatal("DISCORD_WEBHOOK_URL не задан")
}
Утёкший URL позволяет любому писать в ваш канал. См. лучшие практики безопасности о ротации и мониторинге.
Что дальше
Теперь у вас есть всё, чтобы слать сообщения в Discord из любой программы на Go — API-серверов, cron-задач или агентов мониторинга. Чтобы визуально собрать сложную раскладку до написания кода, используйте бесплатный конструктор Discord Webhook: соберите embed, выгрузите JSON и перенесите в свои структуры Go.
Идеи для автоматизации — в статьях уведомления через вебхуки и интеграция с GitHub Actions.
Похожие статьи
- Как получить URL Discord Webhook — Получите URL вебхука на ПК или телефоне
- Как отправить Discord Webhook с помощью Python — Те же паттерны на Python
- Отправка Discord Webhook из JavaScript (Node.js и браузер) — Примеры на Node.js и fetch
- Rate limits Discord Webhook — Как избежать ошибок 429
- Уведомления и автоматизация Discord Webhook — Паттерны алертов мониторинга и CI/CD
Попробуйте в нашем инструменте
Открыть конструктор Discord Webhook