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

Discord Webhook через discord.js (полный гайд по WebhookClient)

Отправка, редактирование, удаление и стриминг файлов через Discord webhook на discord.js WebhookClient. TypeScript, embed'ы, треды, компоненты и rate limit.

discord.jswebhookwebhookclientjavascriptnodejstypescriptтуториалdiscord api
Discord Webhook через discord.js (полный гайд по WebhookClient)

Если уже используете discord.js для бота, в библиотеке есть WebhookClient, который сам делает всё что нужно: знает про rate limits, ретраит, embed builder, file streams. Bot-токен не нужен — достаточно webhook URL.

В гайде — полный жизненный цикл webhook’а на [email protected] (актуальный LTS на 2026): отправка, edit, delete, файлы, треды и подводные камни, на которые ловятся те, кто пришёл с raw fetch.

Установка

npm install discord.js

WebhookClient встроен — отдельный пакет не нужен.

Минимальная отправка

import { WebhookClient } from 'discord.js';

const webhook = new WebhookClient({ url: process.env.WEBHOOK_URL });

await webhook.send({ content: 'Hello from discord.js!' });

Вот и всё. WebhookClient парсит URL, вынимает ID и токен, делает POST. Rate limits — прозрачно: при 429 ждёт и ретраит сам.

Из ID + токена

Если они отдельно:

const webhook = new WebhookClient({
  id: '123456789012345678',
  token: 'abc123-secret-token-xyz',
});

Embed’ы — EmbedBuilder

import { WebhookClient, EmbedBuilder } from 'discord.js';

const webhook = new WebhookClient({ url: process.env.WEBHOOK_URL });

const embed = new EmbedBuilder()
  .setTitle('Деплой завершён')
  .setDescription('Все сервисы здоровы')
  .setColor(0x57f287)
  .setTimestamp()
  .addFields(
    { name: 'Длительность', value: '2м 14с', inline: true },
    { name: 'Commit', value: 'abc1234', inline: true },
  )
  .setFooter({ text: 'CI bot' });

await webhook.send({ embeds: [embed] });

EmbedBuilder валидирует лимиты на этапе сборки — setTitle длиннее 256 символов кидает синхронно, не ждёт 400 от Discord.

Несколько embed’ов

await webhook.send({
  embeds: [
    new EmbedBuilder().setTitle('Service A').setColor(0x57f287),
    new EmbedBuilder().setTitle('Service B').setColor(0xed4245),
  ],
});

До 10 embed’ов на сообщение; общий текст всех ≤ 6000 символов.

Edit и Delete

const message = await webhook.send({ content: 'Работаем…' });
const messageId = message.id;

// Подождали и обновили
await new Promise(r => setTimeout(r, 5000));
await webhook.editMessage(messageId, { content: 'Готово' });

// Потом
await webhook.deleteMessage(messageId);

webhook.send всегда возвращает объект сообщения — никаких ?wait=true query вручную как с raw fetch. Библиотека делает это сама.

Кастомное имя и аватар

await webhook.send({
  content: 'CI build failed',
  username: 'CI Reporter',
  avatarURL: 'https://example.com/ci-avatar.png',
});

Override на одно сообщение; default из настроек webhook’а используется, если не указали.

Файлы — Buffer, Stream, Path

discord.js принимает файлы тремя способами:

import fs from 'node:fs';
import { AttachmentBuilder } from 'discord.js';

// 1. Из локального пути
const file1 = new AttachmentBuilder('./report.pdf', { name: 'report.pdf' });

// 2. Из Buffer
const buf = Buffer.from('обычный текст');
const file2 = new AttachmentBuilder(buf, { name: 'note.txt' });

// 3. Из стрима
const stream = fs.createReadStream('./large.log');
const file3 = new AttachmentBuilder(stream, { name: 'large.log' });

await webhook.send({
  content: 'Прикреплены отчёты',
  files: [file1, file2, file3],
});

Для файлов под 25 МБ — стримы (не грузят файл в память целиком).

Embed с прикреплённой картинкой

Чтобы файл попал в embed, имя + ссылка через attachment://:

const chart = new AttachmentBuilder('./chart.png', { name: 'chart.png' });

const embed = new EmbedBuilder()
  .setTitle('Q4 Revenue')
  .setImage('attachment://chart.png');

await webhook.send({ embeds: [embed], files: [chart] });

Отправка в тред

В тредах (или forum-постах) — threadId:

await webhook.send({
  content: 'Message into thread',
  threadId: '987654321098765432',
});

В forum-каналах можно создать новый пост через threadName:

await webhook.send({
  content: 'New thread body',
  threadName: 'Daily build',  // создаёт новый forum-пост
});

Edit/delete внутри тредов требуют того же threadId:

await webhook.editMessage(messageId, { content: 'updated' }, { threadId });

Components — кнопки и select menu

Webhook может прикреплять интерактивные компоненты:

import {
  WebhookClient,
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
} from 'discord.js';

const webhook = new WebhookClient({ url: process.env.WEBHOOK_URL });

const row = new ActionRowBuilder().addComponents(
  new ButtonBuilder()
    .setURL('https://discord-webhook.com/app')
    .setLabel('Open Builder')
    .setStyle(ButtonStyle.Link),
  new ButtonBuilder()
    .setCustomId('approve')
    .setLabel('Approve')
    .setStyle(ButtonStyle.Success),
);

await webhook.send({
  content: 'Новый деплой готов к approval',
  components: [row],
});

Замечание: webhook может отправить кнопки с customId, но не может обработать клик — для этого нужен бот с listener’ом interaction. Webhook нормально работает с Link-кнопками (без callback), либо в паре с ботом.

Rate Limits — как обрабатывает discord.js

WebhookClient использует общий REST-менеджер библиотеки:

  • Трекает X-RateLimit-Remaining per bucket
  • Очередь следующих запросов при remaining = 0
  • Уважает Retry-After из 429 с auto-retry
  • Backoff на 5xx с экспонентой

Свой retry-loop писать обычно не нужно — webhook.send() в плотном цикле сериализуется библиотекой. Глобальные настройки:

import { REST } from 'discord.js';

const rest = new REST({
  rejectOnRateLimit: ['/channels'],  // кидать вместо ожидания
  retries: 3,
});

В большинстве сценариев default’ы ок. Если шлёте > 30 сообщений/мин на webhook — см. гайд по rate limits для queue-стратегий.

TypeScript

В пакете есть .d.tsWebhookClient полностью типизирован:

import { WebhookClient, type WebhookMessageCreateOptions } from 'discord.js';

const webhook = new WebhookClient({ url: process.env.WEBHOOK_URL! });

const opts: WebhookMessageCreateOptions = {
  content: 'TS-типизированный payload',
  username: 'TS Bot',
};

await webhook.send(opts);

WebhookMessageCreateOptions, EmbedBuilder, AttachmentBuilder — все экспортированы. Автокомплит работает в любом современном TS-редакторе.

Обработка ошибок

Ошибки приходят как DiscordAPIError со структурированными деталями:

import { DiscordAPIError } from 'discord.js';

try {
  await webhook.send({ content: 'x'.repeat(2001) });
} catch (err) {
  if (err instanceof DiscordAPIError) {
    console.error(`Discord отклонил: ${err.code} ${err.message}`);
    // err.code — код Discord (50035 = Invalid Form Body)
    // err.rawError — полное тело ответа
  } else {
    throw err;
  }
}

Частые коды:

  • 50035 — Invalid Form Body (валидация)
  • 10015 — Unknown Webhook (URL невалидный)
  • 10008 — Unknown Message (edit/delete с не тем ID)

Cleanup

WebhookClient держит HTTP keep-alive. Если процессы короткоживущие (Lambda, cron) — закрывайте сокеты через destroy():

await webhook.send({ content: 'final' });
webhook.destroy();

Долгоживущие сервисы — оставляйте открытым; reuse коннекта = быстрые burst-отправки.

Попробуйте вживую

Соберите payload визуально в конструкторе Discord Webhook и скопируйте JSON прямо в webhook.send(...). Для других языков — гайды Python и PHP; для raw-HTTP — JavaScript fetch.

Источники

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

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