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

Отправка Discord Webhook из C# (.NET)

Руководство по отправке Discord webhook сообщений на C# и .NET. Примеры с HttpClient, создание эмбедов, обработка ошибок и паттерны для продакшена.

csharpdotnetwebhookтуториалcsharp discord 2026dotnet discordaspnet discord
Отправка Discord Webhook из C# (.NET)

Discord webhook — это мощный инструмент для отправки автоматических уведомлений в каналы Discord. В этом руководстве мы разберём, как работать с webhook в C# и .NET, от простых сообщений до продвинутых сценариев с эмбедами и обработкой ошибок.

Что такое Discord Webhook

Webhook в Discord — это URL-адрес, который позволяет внешним приложениям отправлять сообщения в канал без использования бота. Это идеальное решение для уведомлений о событиях в вашем приложении: ошибки, успешные деплои, новые заказы или любые другие события. Также через webhook можно создавать опросы, отправлять отложенные сообщения и публиковать в треды и форумы.

Базовая отправка сообщения через HttpClient

Начнём с простейшего примера отправки текстового сообщения. В .NET для HTTP-запросов рекомендуется использовать HttpClient.

using System.Net.Http;
using System.Text;
using System.Text.Json;

public class DiscordWebhookService
{
    private readonly HttpClient _httpClient;
    private readonly string _webhookUrl;

    public DiscordWebhookService(HttpClient httpClient, string webhookUrl)
    {
        _httpClient = httpClient;
        _webhookUrl = webhookUrl;
    }

    public async Task SendMessageAsync(string content)
    {
        var payload = new
        {
            content = content
        };

        var json = JsonSerializer.Serialize(payload);
        var httpContent = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync(_webhookUrl, httpContent);
        response.EnsureSuccessStatusCode();
    }
}

Использование:

var httpClient = new HttpClient();
var webhookUrl = "https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN";
var service = new DiscordWebhookService(httpClient, webhookUrl);

await service.SendMessageAsync("Привет из C#!");

Отправка сообщений с эмбедами

Эмбеды позволяют создавать красиво оформленные сообщения с заголовками, описаниями, полями и цветами. Давайте создадим класс для работы с эмбедами.

public class DiscordEmbed
{
    public string Title { get; set; }
    public string Description { get; set; }
    public int Color { get; set; }
    public List<EmbedField> Fields { get; set; } = new();
    public EmbedFooter Footer { get; set; }
    public string Timestamp { get; set; }
}

public class EmbedField
{
    public string Name { get; set; }
    public string Value { get; set; }
    public bool Inline { get; set; }
}

public class EmbedFooter
{
    public string Text { get; set; }
    public string IconUrl { get; set; }
}

public class DiscordWebhookPayload
{
    public string Content { get; set; }
    public string Username { get; set; }
    public string AvatarUrl { get; set; }
    public List<DiscordEmbed> Embeds { get; set; } = new();
}

Теперь расширим наш сервис для поддержки эмбедов:

public async Task SendEmbedAsync(DiscordWebhookPayload payload)
{
    var options = new JsonSerializerOptions
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
    };

    var json = JsonSerializer.Serialize(payload, options);
    var httpContent = new StringContent(json, Encoding.UTF8, "application/json");

    var response = await _httpClient.PostAsync(_webhookUrl, httpContent);
    response.EnsureSuccessStatusCode();
}

Пример использования для уведомления об ошибке:

var payload = new DiscordWebhookPayload
{
    Username = "Система мониторинга",
    Embeds = new List<DiscordEmbed>
    {
        new DiscordEmbed
        {
            Title = "Критическая ошибка",
            Description = "Обнаружена ошибка в модуле обработки платежей",
            Color = 15158332, // Красный цвет
            Fields = new List<EmbedField>
            {
                new EmbedField
                {
                    Name = "Сервис",
                    Value = "PaymentProcessor",
                    Inline = true
                },
                new EmbedField
                {
                    Name = "Окружение",
                    Value = "Production",
                    Inline = true
                },
                new EmbedField
                {
                    Name = "Сообщение об ошибке",
                    Value = "NullReferenceException at line 142",
                    Inline = false
                }
            },
            Footer = new EmbedFooter
            {
                Text = "Система мониторинга v2.0"
            },
            Timestamp = DateTime.UtcNow.ToString("o")
        }
    }
};

await service.SendEmbedAsync(payload);

Подробнее о работе с цветами эмбедов читайте в нашей статье Цвета Discord Embed.

Отправка файлов

Discord webhook поддерживает отправку файлов вместе с сообщениями. Для этого используется multipart/form-data.

public async Task SendFileAsync(string filePath, string message = null)
{
    using var form = new MultipartFormDataContent();
    
    // Добавляем файл
    var fileContent = new ByteArrayContent(await File.ReadAllBytesAsync(filePath));
    fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
    form.Add(fileContent, "file", Path.GetFileName(filePath));
    
    // Добавляем JSON payload если есть сообщение
    if (!string.IsNullOrEmpty(message))
    {
        var payload = new { content = message };
        var json = JsonSerializer.Serialize(payload);
        form.Add(new StringContent(json, Encoding.UTF8, "application/json"), "payload_json");
    }

    var response = await _httpClient.PostAsync(_webhookUrl, form);
    response.EnsureSuccessStatusCode();
}

Использование:

await service.SendFileAsync("logs/error-2026-02-15.log", "Лог-файл с ошибками за сегодня");

Обработка ошибок и повторные попытки

В продакшен-коде важно правильно обрабатывать ошибки и реализовать механизм повторных попыток при временных сбоях.

using Polly;
using Polly.Extensions.Http;

public class RobustDiscordWebhookService
{
    private readonly HttpClient _httpClient;
    private readonly string _webhookUrl;
    private readonly IAsyncPolicy<HttpResponseMessage> _retryPolicy;

    public RobustDiscordWebhookService(HttpClient httpClient, string webhookUrl)
    {
        _httpClient = httpClient;
        _webhookUrl = webhookUrl;
        
        // Политика повторных попыток с экспоненциальной задержкой
        _retryPolicy = HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
            .WaitAndRetryAsync(3, retryAttempt => 
                TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
    }

    public async Task<bool> SendMessageAsync(string content)
    {
        try
        {
            var payload = new { content = content };
            var json = JsonSerializer.Serialize(payload);
            var httpContent = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await _retryPolicy.ExecuteAsync(async () =>
                await _httpClient.PostAsync(_webhookUrl, httpContent));

            if (response.IsSuccessStatusCode)
            {
                return true;
            }

            // Логируем ошибку
            var errorContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"Discord webhook failed: {response.StatusCode} - {errorContent}");
            return false;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception sending Discord webhook: {ex.Message}");
            return false;
        }
    }
}

Интеграция с Dependency Injection в ASP.NET Core

Для ASP.NET Core приложений рекомендуется регистрировать webhook-сервис через DI контейнер.

В Program.cs или Startup.cs:

builder.Services.AddHttpClient<IDiscordWebhookService, DiscordWebhookService>((serviceProvider, client) =>
{
    var configuration = serviceProvider.GetRequiredService<IConfiguration>();
    var webhookUrl = configuration["Discord:WebhookUrl"];
    
    client.BaseAddress = new Uri(webhookUrl);
    client.Timeout = TimeSpan.FromSeconds(30);
});

// Или с Polly для retry политики
builder.Services.AddHttpClient<IDiscordWebhookService, DiscordWebhookService>()
    .AddPolicyHandler(HttpPolicyExtensions
        .HandleTransientHttpError()
        .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))));

Интерфейс сервиса:

public interface IDiscordWebhookService
{
    Task<bool> SendMessageAsync(string content);
    Task<bool> SendEmbedAsync(DiscordWebhookPayload payload);
    Task<bool> SendFileAsync(string filePath, string message = null);
}

Использование в контроллере:

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    private readonly IDiscordWebhookService _discordService;

    public OrdersController(IDiscordWebhookService discordService)
    {
        _discordService = discordService;
    }

    [HttpPost]
    public async Task<IActionResult> CreateOrder([FromBody] OrderDto order)
    {
        // Логика создания заказа
        var orderId = await ProcessOrder(order);

        // Отправляем уведомление в Discord
        var payload = new DiscordWebhookPayload
        {
            Embeds = new List<DiscordEmbed>
            {
                new DiscordEmbed
                {
                    Title = "Новый заказ",
                    Color = 3066993, // Зелёный
                    Fields = new List<EmbedField>
                    {
                        new EmbedField { Name = "ID заказа", Value = orderId.ToString(), Inline = true },
                        new EmbedField { Name = "Сумма", Value = $"{order.Total:C}", Inline = true },
                        new EmbedField { Name = "Клиент", Value = order.CustomerName, Inline = false }
                    },
                    Timestamp = DateTime.UtcNow.ToString("o")
                }
            }
        };

        await _discordService.SendEmbedAsync(payload);

        return Ok(new { orderId });
    }
}

Реальный пример: система мониторинга приложения

Давайте создадим полноценный сервис мониторинга, который отправляет различные типы уведомлений.

public class ApplicationMonitoringService
{
    private readonly IDiscordWebhookService _discord;
    private readonly ILogger<ApplicationMonitoringService> _logger;

    public ApplicationMonitoringService(
        IDiscordWebhookService discord,
        ILogger<ApplicationMonitoringService> logger)
    {
        _discord = discord;
        _logger = logger;
    }

    public async Task NotifyErrorAsync(Exception exception, string context)
    {
        var embed = new DiscordEmbed
        {
            Title = "Ошибка приложения",
            Description = exception.Message,
            Color = 15158332, // Красный
            Fields = new List<EmbedField>
            {
                new EmbedField
                {
                    Name = "Тип исключения",
                    Value = exception.GetType().Name,
                    Inline = true
                },
                new EmbedField
                {
                    Name = "Контекст",
                    Value = context,
                    Inline = true
                },
                new EmbedField
                {
                    Name = "Stack Trace",
                    Value = $"```{exception.StackTrace?.Substring(0, Math.Min(1000, exception.StackTrace.Length ?? 0))}```",
                    Inline = false
                }
            },
            Timestamp = DateTime.UtcNow.ToString("o")
        };

        var payload = new DiscordWebhookPayload
        {
            Content = "@here Критическая ошибка!",
            Embeds = new List<DiscordEmbed> { embed }
        };

        await _discord.SendEmbedAsync(payload);
    }

    public async Task NotifyDeploymentAsync(string version, string environment, bool success)
    {
        var embed = new DiscordEmbed
        {
            Title = success ? "Деплой успешен" : "Деплой провалился",
            Color = success ? 3066993 : 15158332, // Зелёный или красный
            Fields = new List<EmbedField>
            {
                new EmbedField { Name = "Версия", Value = version, Inline = true },
                new EmbedField { Name = "Окружение", Value = environment, Inline = true }
            },
            Timestamp = DateTime.UtcNow.ToString("o")
        };

        await _discord.SendEmbedAsync(new DiscordWebhookPayload
        {
            Embeds = new List<DiscordEmbed> { embed }
        });
    }

    public async Task NotifyPerformanceIssueAsync(string metric, double value, double threshold)
    {
        var embed = new DiscordEmbed
        {
            Title = "Проблема производительности",
            Description = $"Метрика **{metric}** превысила порог",
            Color = 16776960, // Жёлтый
            Fields = new List<EmbedField>
            {
                new EmbedField { Name = "Текущее значение", Value = value.ToString("F2"), Inline = true },
                new EmbedField { Name = "Порог", Value = threshold.ToString("F2"), Inline = true }
            },
            Timestamp = DateTime.UtcNow.ToString("o")
        };

        await _discord.SendEmbedAsync(new DiscordWebhookPayload
        {
            Embeds = new List<DiscordEmbed> { embed }
        });
    }
}

Лучшие практики

1. Используйте IHttpClientFactory

Никогда не создавайте HttpClient напрямую в продакшен-коде. Используйте IHttpClientFactory для избежания исчерпания сокетов.

2. Храните webhook URL в конфигурации

{
  "Discord": {
    "WebhookUrl": "https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN",
    "ErrorWebhookUrl": "https://discord.com/api/webhooks/ERROR_ID/ERROR_TOKEN"
  }
}

3. Не блокируйте основной поток

Всегда используйте асинхронные методы и не вызывайте .Result или .Wait().

4. Соблюдайте rate limits

Discord ограничивает количество запросов. Для webhook это обычно 30 запросов в минуту. Используйте очереди для больших объёмов.

public class RateLimitedDiscordService
{
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
    private DateTime _lastRequest = DateTime.MinValue;
    private readonly TimeSpan _minInterval = TimeSpan.FromSeconds(2);

    public async Task SendWithRateLimitAsync(Func<Task> sendAction)
    {
        await _semaphore.WaitAsync();
        try
        {
            var timeSinceLastRequest = DateTime.UtcNow - _lastRequest;
            if (timeSinceLastRequest < _minInterval)
            {
                await Task.Delay(_minInterval - timeSinceLastRequest);
            }

            await sendAction();
            _lastRequest = DateTime.UtcNow;
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

5. Валидируйте данные перед отправкой

Discord имеет ограничения на размеры полей:

  • Контент сообщения: 2000 символов
  • Заголовок эмбеда: 256 символов
  • Описание эмбеда: 4096 символов
  • Количество полей: 25
  • Имя поля: 256 символов
  • Значение поля: 1024 символа
public static class DiscordValidator
{
    public static string TruncateContent(string content, int maxLength = 2000)
    {
        if (string.IsNullOrEmpty(content)) return content;
        return content.Length <= maxLength 
            ? content 
            : content.Substring(0, maxLength - 3) + "...";
    }

    public static void ValidateEmbed(DiscordEmbed embed)
    {
        if (embed.Title?.Length > 256)
            throw new ArgumentException("Заголовок эмбеда не может быть длиннее 256 символов");
        
        if (embed.Description?.Length > 4096)
            throw new ArgumentException("Описание эмбеда не может быть длиннее 4096 символов");
        
        if (embed.Fields?.Count > 25)
            throw new ArgumentException("Эмбед не может содержать больше 25 полей");
    }
}

Тестирование webhook

Для тестирования webhook-интеграции используйте mock-объекты:

public class DiscordWebhookServiceTests
{
    [Fact]
    public async Task SendMessage_ValidContent_ReturnsSuccess()
    {
        // Arrange
        var mockHttp = new Mock<HttpMessageHandler>();
        mockHttp.Protected()
            .Setup<Task<HttpResponseMessage>>("SendAsync", 
                ItExpr.IsAny<HttpRequestMessage>(),
                ItExpr.IsAny<CancellationToken>())
            .ReturnsAsync(new HttpResponseMessage
            {
                StatusCode = System.Net.HttpStatusCode.NoContent
            });

        var httpClient = new HttpClient(mockHttp.Object);
        var service = new DiscordWebhookService(httpClient, "https://test.webhook");

        // Act
        var result = await service.SendMessageAsync("Test message");

        // Assert
        Assert.True(result);
    }
}

Визуальный конструктор эмбедов

Создание сложных эмбедов в коде может быть утомительным. Используйте наш бесплатный визуальный конструктор для проектирования эмбедов и генерации JSON, который можно десериализовать в C# объекты.

Больше информации о создании эмбедов читайте в статье Руководство по Discord Embed Builder.

Заключение

Discord webhook в C# — это мощный инструмент для интеграции уведомлений в ваши .NET приложения. Следуя приведённым примерам и лучшим практикам, вы сможете создать надёжную систему уведомлений для мониторинга, алертов и автоматизации.

Основные моменты:

  • Используйте HttpClient через DI контейнер
  • Реализуйте обработку ошибок и retry-логику
  • Соблюдайте rate limits Discord
  • Валидируйте данные перед отправкой
  • Используйте эмбеды для красивого оформления

Для более сложных сценариев автоматизации изучите нашу статью Автоматизация Discord Webhook.

Помимо кода, discord-webhook.com предлагает визуальный конструктор с такими функциями как отложенные сообщения, поддержка тредов и форумов, опросы и интерактивные кнопки с действиями — всё без написания кода.

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

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

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