Отправка Discord Webhook из C# (.NET)
Руководство по отправке Discord webhook сообщений на C# и .NET. Примеры с HttpClient, создание эмбедов, обработка ошибок и паттерны для продакшена.
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 Embed Builder
- Цвета Discord Embed: полный справочник
- Автоматизация уведомлений Discord Webhook
- Отложенные сообщения Discord Webhook — Планирование отправки сообщений по расписанию из C#
- Интерактивные кнопки и действия Discord — Добавление интерактивных элементов в webhook-сообщения
Попробуйте в нашем инструменте
Открыть конструктор Discord Webhook