Перейти к содержимому
Все статьи
· Обновлено 3 июля 2026 г.

Как отправить Discord Webhook на Go (Golang)

Практическое руководство по отправке сообщений Discord webhook на Go через net/http. Текст, embed, файлы и обработка rate limit — без внешних библиотек.

golanggowebhookтуториалdiscord apigo discord 2026net/http

🇬🇧 Доступно также на English

Как отправить Discord Webhook на Go (Golang)

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.

Похожие статьи

Попробуйте в нашем инструменте

Открыть конструктор Discord Webhook