Интерактивные кнопки в Discord: полное руководство для разработчиков

Иван Корнев·26.04.2026·4 мин

Чтобы добавить кнопки в сообщение бота Discord, необходимо использовать Интерактивные компоненты (Interactive Components) API. Вместо отправки обычного текста бот формирует сообщение с прикрепленными элементами ActionRow, содержащими кнопки (Button). При нажатии пользователь генерирует событие interaction, которое бот должен перехватить, проверить и обработать, обновив сообщение или отправив ответ.

Это руководство поможет вам внедрить кнопки в ваших проектах на Python (discord.py) и JavaScript (discord.js), избегая распространенных ошибок и соблюдая лимиты API.

Ключевое отличие: Кнопки не являются частью текста сообщения. Это отдельные UI-элементы, которые рендерятся клиентом Discord под сообщением. Они требуют активной обработки событий на стороне вашего сервера или хостинга бота.

Архитектура интерактивных компонентов

Работа с кнопками строится на трех основных этапах:

  1. Конструирование: Создание объекта кнопки (стиль, метка, ID) и размещение её в строке действия (ActionRow).
  2. Отправка: Привязка созданного компонента к сообщению при его отправке (send) или редактировании (edit).
  3. Обработка: Прослушивание события взаимодействия (interactionCreate). Бот проверяет custom_id нажатой кнопки и выполняет соответствующий код.

Ограничения API Discord

  • Максимум 5 кнопок в одной строке (ActionRow).
  • Максимум 5 строк в одном сообщении.
  • Итого: не более 25 кнопок на одно сообщение.
  • Длина custom_id (уникального идентификатора) не должна превышать 100 символов.

Реализация на Python (библиотека discord.py)

В современных версиях discord.py (2.0+) используется система View. Это объектно-ориентированный подход, где состояние кнопок хранится в классе.

Шаг 1: Создание класса View

import discord
from discord.ui import Button, View

class MyView(View):
    def __init__(self):
        super().__init__(timeout=60) # Кнопки исчезнут через 60 секунд

    @discord.ui.button(label="Нажми меня", style=discord.ButtonStyle.primary)
    async def first_button_callback(self, interaction: discord.Interaction, button: Button):
        await interaction.response.send_message("Вы нажали первую кнопку!", ephemeral=True)
        # Ephemeral=True делает ответ видимым только для нажавшего

    @discord.ui.button(label="Стоп", style=discord.ButtonStyle.danger)
    async def stop_button_callback(self, interaction: discord.Interaction, button: Button):
        self.stop() # Останавливает ожидание взаимодействий
        await interaction.response.edit_message(content="Действие остановлено.", view=None)

Шаг 2: Отправка сообщения с кнопками

@bot.command()
async def test_buttons(ctx):
    view = MyView()
    await ctx.send("Выберите действие:", view=view)

Используйте timeout в конструкторе View, чтобы избежать утечек памяти. Если пользователь не нажимает кнопки долгое время, объект View будет уничтожен автоматически.

Реализация на JavaScript (библиотека discord.js v14+)

В discord.js версии 14 и выше компоненты создаются через билдеры (Builders). Обработка происходит через глобальный слушатель событий.

Шаг 1: Создание и отправка кнопок

const { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } = require('discord.js');

// Создаем кнопки
const acceptButton = new ButtonBuilder()
    .setCustomId('accept_action')
    .setLabel('Принять')
    .setStyle(ButtonStyle.Success);

const declineButton = new ButtonBuilder()
    .setCustomId('decline_action')
    .setLabel('Отклонить')
    .setStyle(ButtonStyle.Danger);

// Собираем ряд действий
const row = new ActionRowBuilder()
    .addComponents(acceptButton, declineButton);

// Отправляем сообщение
await channel.send({
    content: 'Подтвердите ваше действие:',
    components: [row]
});

Шаг 2: Обработка нажатий (InteractionCreate)

client.on('InteractionCreate', async interaction => {
    if (!interaction.isButton()) return;

    if (interaction.customId === 'accept_action') {
        await interaction.reply({ content: 'Действие принято!', ephemeral: true });
    } else if (interaction.customId === 'decline_action') {
        await interaction.update({ content: 'Действие отменено.', components: [] });
    }
});

Сравнение стилей кнопок

Правильный выбор цвета помогает пользователю интуитивно понять назначение кнопки.

Стиль (Enum)ЦветНазначение
PrimaryСинийОсновное действие (например, "Подробнее", "Далее").
SecondaryСерыйНейтральные действия (например, "Назад", "Отмена").
SuccessЗеленыйПозитивные подтверждения ("Принять", "Купить").
DangerКрасныйОпасные или необратимые действия ("Удалить", "Бан").
LinkСерый с цепьюВнешняя ссылка (не вызывает событие взаимодействия).

Кнопки типа Link не отправляют событие interaction вашему боту. Вы не можете отследить, нажал ли пользователь на ссылку, и не можете изменить состояние такой кнопки после отправки.

Частые ошибки разработчиков

  1. Игнорирование ephemeral ответов. Если бот отвечает на нажатие кнопки обычным сообщением, это засоряет чат для всех участников канала. Используйте ephemeral=True (Python) или ephemeral: true (JS), если ответ предназначен только для нажавшего.

  2. Отсутствие проверки прав доступа. Любой пользователь может нажать кнопку. Если кнопка выполняет административное действие, обязательно проверяйте роли пользователя внутри обработчика interaction перед выполнением логики.

  3. "Зависание" кнопок после перезагрузки бота. Если бот перезагружается, он "забывает" о существовании отправленных ранее View.

    • Решение для discord.py: Используйте персистентные вью (Persistent Views) с явной регистрацией custom_id.
    • Решение для discord.js: Храните состояние ожидающих взаимодействий в базе данных или убедитесь, что логика обработчика interactionCreate самодостаточна.
  4. Превышение лимита символов в Custom ID. Не пытайтесь зашить всю информацию о состоянии в custom_id. Если данных много, используйте базу данных или кэш, сохраняя в custom_id только короткий ключ (например, ticket_open_123).

FAQ

Можно ли добавить кнопку в уже отправленное сообщение? Да, используя метод редактирования сообщения (edit_message или interaction.update). Вы должны передать полный массив компонентов заново. Нельзя просто "добавить" одну кнопку к существующему ряду — нужно переопределить весь набор компонентов.

Почему кнопки становятся серыми и неактивными? Это происходит, если истек timeout, указанный при создании View, или если бот был перезапущен без использования персистентных компонентов. Также кнопки деактивируются, если вы явно передадите disabled=True при обновлении сообщения.

Безопасны ли кнопки от спама? Discord имеет встроенные рейт-лимиты на взаимодействия. Однако рекомендуется добавлять собственную проверку: игнорировать повторные нажатия на ту же кнопку, если предыдущая операция еще не завершена (например, блокировать кнопку на время выполнения запроса к БД).