Интерактивные кнопки в Discord: полное руководство для разработчиков
Чтобы добавить кнопки в сообщение бота Discord, необходимо использовать Интерактивные компоненты (Interactive Components) API. Вместо отправки обычного текста бот формирует сообщение с прикрепленными элементами ActionRow, содержащими кнопки (Button). При нажатии пользователь генерирует событие interaction, которое бот должен перехватить, проверить и обработать, обновив сообщение или отправив ответ.
Это руководство поможет вам внедрить кнопки в ваших проектах на Python (discord.py) и JavaScript (discord.js), избегая распространенных ошибок и соблюдая лимиты API.
Ключевое отличие: Кнопки не являются частью текста сообщения. Это отдельные UI-элементы, которые рендерятся клиентом Discord под сообщением. Они требуют активной обработки событий на стороне вашего сервера или хостинга бота.
Архитектура интерактивных компонентов
Работа с кнопками строится на трех основных этапах:
- Конструирование: Создание объекта кнопки (стиль, метка, ID) и размещение её в строке действия (
ActionRow). - Отправка: Привязка созданного компонента к сообщению при его отправке (
send) или редактировании (edit). - Обработка: Прослушивание события взаимодействия (
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 вашему боту. Вы не можете отследить, нажал ли пользователь на ссылку, и не можете изменить состояние такой кнопки после отправки.
Частые ошибки разработчиков
-
Игнорирование
ephemeralответов. Если бот отвечает на нажатие кнопки обычным сообщением, это засоряет чат для всех участников канала. Используйтеephemeral=True(Python) илиephemeral: true(JS), если ответ предназначен только для нажавшего. -
Отсутствие проверки прав доступа. Любой пользователь может нажать кнопку. Если кнопка выполняет административное действие, обязательно проверяйте роли пользователя внутри обработчика
interactionперед выполнением логики. -
"Зависание" кнопок после перезагрузки бота. Если бот перезагружается, он "забывает" о существовании отправленных ранее
View.- Решение для discord.py: Используйте персистентные вью (Persistent Views) с явной регистрацией
custom_id. - Решение для discord.js: Храните состояние ожидающих взаимодействий в базе данных или убедитесь, что логика обработчика
interactionCreateсамодостаточна.
- Решение для discord.py: Используйте персистентные вью (Persistent Views) с явной регистрацией
-
Превышение лимита символов в Custom ID. Не пытайтесь зашить всю информацию о состоянии в
custom_id. Если данных много, используйте базу данных или кэш, сохраняя вcustom_idтолько короткий ключ (например,ticket_open_123).
FAQ
Можно ли добавить кнопку в уже отправленное сообщение?
Да, используя метод редактирования сообщения (edit_message или interaction.update). Вы должны передать полный массив компонентов заново. Нельзя просто "добавить" одну кнопку к существующему ряду — нужно переопределить весь набор компонентов.
Почему кнопки становятся серыми и неактивными?
Это происходит, если истек timeout, указанный при создании View, или если бот был перезапущен без использования персистентных компонентов. Также кнопки деактивируются, если вы явно передадите disabled=True при обновлении сообщения.
Безопасны ли кнопки от спама? Discord имеет встроенные рейт-лимиты на взаимодействия. Однако рекомендуется добавлять собственную проверку: игнорировать повторные нажатия на ту же кнопку, если предыдущая операция еще не завершена (например, блокировать кнопку на время выполнения запроса к БД).