Back to How To
How To

Как запустить Discord-бота в сети с ограниченным доступом

Настройте приём Discord Interactions и отправку ответов через Adal, если прямые обращения к адресам Discord ограничены политикой вашей сети.

30 min

Если системный администратор ограничил прямой доступ к адресам Discord из рабочей или серверной сети, это не обязательно означает, что интеграцию придётся запускать на другом компьютере или менять правила сети.

Для ботов, работающих через HTTP Interactions, можно использовать Adal как согласованный промежуточный маршрут. Discord будет отправлять события на публичный HTTPS-сервер Adal, Adal CLI передаст их локальному приложению, а ответы бота можно отправлять обратно через отдельный сервер Adal с получателем Direct HTTP.

При этом локальное приложение не обращается к адресам Discord напрямую. Оно устанавливает исходящее соединение только с инфраструктурой Adal, разрешённой администратором сети.

Важно: такая схема должна использоваться с ведома владельца сети. Adal не изменяет и не маскирует сетевой трафик, а предоставляет управляемую точку приёма, журнал доставки и явный маршрут между сервисами.

Что получится в итоге

Схема приёма событий будет выглядеть так:

Пользователь Discord ↓ Discord Interactions ↓ HTTPS POST Входящий сервер Adal ↓ Adal CLI ↓ Локальное приложение бота

Ответы бота пойдут в обратном направлении через отдельный сервер:

Локальное приложение ↓ HTTPS POST Исходящий сервер Adal ↓ Direct HTTP Discord Interaction Callback ↓ Пользователь Discord

В результате приложение сможет:

  • принимать slash-команды и другие Discord Interactions;

  • проверять подпись каждого запроса;

  • отправлять первоначальные ответы;

  • отправлять последующие сообщения;

  • просматривать историю запросов и доставок в Adal;

  • работать без прямого обращения локального процесса к доменам Discord.

Для каких Discord-ботов подходит эта схема

Discord позволяет получать Interactions двумя способами:

  • через Gateway;

  • через публичный HTTP Interaction Endpoint.

В этой инструкции используется второй вариант. Он подходит для:

  • slash-команд;

  • нажатий на кнопки;

  • выбора пунктов меню;

  • отправки форм;

  • других компонентов, создающих Discord Interaction.

Adal в этом сценарии не заменяет Discord Gateway. Если бот должен постоянно получать события вроде MESSAGE_CREATE, изменения участников сервера или presence-события через Gateway WebSocket, потребуется отдельная согласованная с администратором сетевая схема.

Что потребуется

Перед началом убедитесь, что у вас есть:

  • приложение в Discord Developer Portal;

  • Discord Application ID;

  • Public Key приложения;

  • аккаунт Adal;

  • установленный Adal CLI;

  • локальное приложение с HTTP-обработчиком;

  • разрешение администратора на исходящие HTTPS- и WSS-соединения с Adal.

Для примера будем считать, что локальное приложение работает по адресу:

http://127.0.0.1:3000

А Discord Interactions обрабатываются по маршруту:

http://127.0.0.1:3000/discord/interactions

Шаг 1. Подготовьте приложение Discord

Откройте Discord Developer Portal и выберите существующее приложение либо создайте новое.

Сохраните следующие значения:

  • Application ID — идентификатор приложения;

  • Public Key — публичный ключ для проверки подписей;

  • при необходимости — Bot Token для операций Discord API.

Секреты и токены не следует добавлять в Git, вставлять во frontend-код или передавать в открытых сообщениях.

Сохраните публичный ключ приложения в переменную окружения:

export DISCORD_PUBLIC_KEY='public-key-from-developer-portal'

Application ID также понадобится при формировании адресов последующих ответов:

export DISCORD_APPLICATION_ID='your-application-id'

Шаг 2. Запустите локальный обработчик Interactions

Локальное приложение должно принимать POST-запросы на маршрут:

/discord/interactions

Каждый запрос Discord содержит заголовки:

X-Signature-Ed25519 X-Signature-Timestamp

Приложение должно проверить подпись по исходному, ещё не изменённому телу запроса. Нельзя сначала разобрать JSON, собрать его заново и проверять уже преобразованную строку: подпись рассчитана по оригинальным байтам.

После проверки приложение обрабатывает поле type.

При проверке Interaction Endpoint Discord отправляет запрос типа PING. На него нужно сформировать ответ типа PONG:

{ "type": 1 }

Для команды первоначальный ответ может выглядеть так:

{ "type": 4, "data": { "content": "Команда получена" } }

Локальный обработчик не должен принимать запросы с некорректной подписью, даже если они были доставлены через известный сервер Adal.

Шаг 3. Создайте входящий сервер в Adal

Откройте Dashboard Adal и создайте сервер для приёма Discord Interactions.

Рекомендуемые настройки:

  • Name: discord-interactions-in;

  • Allowed methods: POST;

  • Deliver pending requests on connect: включено для разработки;

  • Notes: Discord HTTP Interactions.

После создания сервер получит постоянный публичный адрес:

https://your-inbound-server.se1.adal.cloud

Сохраните выданный CLI token. Он отображается только один раз. Если токен будет потерян или раскрыт, создайте новый.

Шаг 4. Добавьте локальную Destination

Внутри входящего сервера создайте получателя со следующими параметрами:

  • Delivery method: Adal CLI;

  • URL: http://127.0.0.1:3000;

  • Name: discord-local-bot.

Adal сохраняет путь исходного запроса. Поэтому запрос, принятый по адресу:

https://your-inbound-server.se1.adal.cloud/discord/interactions

будет передан локальному приложению по адресу:

http://127.0.0.1:3000/discord/interactions

Запустите Adal CLI с токеном входящего сервера:

adalcli --token <ваш-токен-cli>

После подключения Destination должна перейти в состояние online.

CLI устанавливает исходящее защищённое соединение с Adal. Открывать входящий порт на рабочем компьютере или публиковать локальное приложение не требуется.

Шаг 5. Укажите Interaction Endpoint в Discord

В настройках приложения Discord укажите:

https://your-inbound-server.se1.adal.cloud/discord/interactions

Discord отправит проверочный PING и ожидает корректно подписанный и своевременно обработанный ответ.

Для Discord особенно важно время первоначального ответа. Interaction необходимо подтвердить в пределах установленного Discord короткого тайм-аута — обычно около трёх секунд. Поэтому перед использованием схемы в production проверьте суммарную задержку маршрута:

Discord → Adal → Adal CLI → приложение → Adal → Discord

Если обработка команды занимает больше времени, сначала отправьте отложенный ответ, а основную работу выполните после подтверждения Interaction.

Шаг 6. Создайте сервер для отправки ответов

Локальный компьютер может принимать события через Adal CLI, но при ограниченном прямом доступе он также не сможет обратиться к Discord Interaction Callback.

Для исходящих запросов создайте второй сервер Adal:

  • Name: discord-interactions-out;

  • Allowed methods: POST, а при необходимости PATCH и DELETE;

  • Notes: Discord interaction responses.

Затем добавьте к нему получателя:

  • Delivery method: Direct HTTP;

  • URL: https://discord.com;

  • Name: discord-api.

Этот сервер должен быть отдельным от входящего. Иначе полученные от Discord запросы могут быть одновременно направлены и локальному приложению, и обратно на адреса Discord.

Шаг 7. Отправьте первоначальный ответ

В каждом Interaction Discord передаёт:

  • id — идентификатор Interaction;

  • token — временный токен ответа.

Локальное приложение формирует путь callback:

/api/v10/interactions/{interaction.id}/{interaction.token}/callback

Но отправляет запрос не напрямую на discord.com, а на исходящий сервер Adal:

https://your-outbound-server.se1.adal.cloud/api/v10/interactions/{interaction.id}/{interaction.token}/callback

Тело запроса:

{ "type": 4, "data": { "content": "Бот запущен и получил вашу команду" } }

Adal примет запрос и доставит его получателю Direct HTTP. Поскольку получателем указан адрес:

https://discord.com

итоговая доставка будет выполнена на:

https://discord.com/api/v10/interactions/{interaction.id}/{interaction.token}/callback

Путь и query-параметры исходного запроса сохраняются.

Шаг 8. Отправьте последующее сообщение

После первоначального ответа бот может отправить follow-up через webhook Interaction.

Путь Discord имеет следующий вид:

/api/v10/webhooks/{application.id}/{interaction.token}

Через исходящий сервер Adal приложение обращается к адресу:

https://your-outbound-server.se1.adal.cloud/api/v10/webhooks/{application.id}/{interaction.token}

Пример тела запроса:

{ "content": "Задача завершена" }

Adal доставит его на соответствующий URL Discord через Direct HTTP.

Токен Interaction является секретом и временно даёт право отправлять ответы от имени конкретного взаимодействия. Не записывайте полный callback URL в общедоступные логи и не публикуйте его в сообщениях.

Почему используются два сервера Adal

Входящий и исходящий маршруты решают разные задачи.

Входящий сервер:

Discord → Adal → Adal CLI → localhost

Исходящий сервер:

localhost → Adal → Direct HTTP → Discord

Разделение позволяет:

  • отдельно ограничить разрешённые HTTP-методы;

  • независимо просматривать историю приёма и отправки;

  • не смешивать Discord Interactions с callback-запросами;

  • выдавать разные токены и правила доступа;

  • быстрее определить, на каком участке произошла ошибка.

Для production рекомендуется также использовать отдельные серверы для разных Discord-приложений и окружений.

Работа в сети с ограниченным доступом

Если адреса Discord ограничены политикой организации, сначала согласуйте с администратором использование Adal.

В типовой конфигурации администратору не требуется открывать доступ ко всему Discord для компьютера разработчика. Достаточно разрешить исходящие соединения локального приложения и Adal CLI с необходимыми доменами Adal.

Само обращение к Discord выполняется из региона Adal через получателя Direct HTTP.

Это создаёт явный и проверяемый маршрут:

Рабочая сеть → Adal → Discord

В Dashboard остаются:

  • история принятых запросов;

  • результаты доставки;

  • HTTP-статусы;

  • количество попыток;

  • время обработки;

  • ошибки соединения.

Такая схема полезна, когда администратору важно сохранить ограничения сети, но разрешить конкретную серверную интеграцию.

Безопасность

Всегда проверяйте подпись Discord

Публичный URL входящего сервера может получить запрос не только от Discord. Единственный надёжный способ подтвердить источник Interaction — проверить Ed25519-подпись по Public Key приложения.

Запрос с отсутствующей или некорректной подписью должен получать ответ 401 Unauthorized.

Не раскрывайте Interaction token

Токен входит в callback URL и позволяет отправлять ответы на конкретное Interaction. Не выводите полный URL:

  • в публичные логи;

  • в сообщения об ошибках;

  • в системы аналитики;

  • в screenshots и документацию.

Ограничьте методы серверов

Для входящего сервера обычно достаточно POST.

Для исходящего сервера разрешайте только методы, которые действительно использует приложение. Чем уже конфигурация, тем проще контролировать интеграцию.

Разделяйте окружения

Создайте отдельные серверы Adal для разработки и production:

discord-dev-in discord-dev-out discord-prod-in discord-prod-out

Так тестовый бот не сможет случайно использовать маршруты production-приложения.

Учитывайте повторную доставку

Webhook может быть доставлен повторно. Приложение должно сохранять идентификатор Interaction и не выполнять необратимое действие несколько раз.

Особенно это важно для команд, которые:

  • создают записи;

  • отправляют уведомления;

  • запускают сборку;

  • меняют права;

  • выполняют платёжные или административные операции.

Частые проблемы

Discord не принимает Interaction Endpoint

Проверьте:

  1. URL использует HTTPS.

  2. Adal CLI подключён.

  3. Локальная Destination находится online.

  4. Приложение обрабатывает PING.

  5. Подпись проверяется по исходному телу запроса.

  6. Ответ укладывается в тайм-аут Discord.

История сервера Adal покажет, был ли проверочный запрос принят и доставлен локальному приложению.

Interaction приходит, но пользователь видит ошибку

Чаще всего первоначальный ответ не успел дойти до Discord вовремя.

Не выполняйте длительную бизнес-логику до подтверждения Interaction. Сначала отправьте немедленный или отложенный ответ, затем продолжайте обработку.

Также проверьте историю исходящего сервера Adal и HTTP-статус доставки в Discord.

Входящий запрос отвечает 401 Unauthorized

Проверьте:

  • значение DISCORD_PUBLIC_KEY;

  • заголовки X-Signature-Ed25519 и X-Signature-Timestamp;

  • использование оригинального тела запроса;

  • отсутствие middleware, которое изменяет тело до проверки подписи.

Запрос принят исходящим сервером, но Discord не показывает сообщение

Откройте историю доставки Direct HTTP и проверьте ответ Discord.

Возможные причины:

  • неверный interaction.id;

  • просроченный или некорректный interaction.token;

  • неправильный Application ID;

  • первоначальный callback отправлен слишком поздно;

  • неверный тип Interaction Response;

  • путь не соответствует Discord API.

После перезапуска компьютера бот перестал получать команды

Публичные URL серверов Adal остаются прежними, но локальное приложение и Adal CLI нужно запустить заново.

Проверьте:

локальное приложение → Adal CLI → состояние Destination

Если доставка ожидающих запросов включена, накопленные события могут быть переданы после восстановления соединения.

Ограничения схемы

Этот способ предназначен для Discord HTTP Interactions и webhook-вызовов.

Он не предоставляет локальному приложению универсальный доступ к Discord и не заменяет:

  • Discord Gateway;

  • голосовые подключения;

  • загрузку произвольных ресурсов Discord;

  • общий HTTP-прокси;

  • пользовательское приложение Discord.

Если боту нужны Gateway-события, следует отдельно согласовать их сетевую архитектуру с администратором.

Также необходимо проверить задержку первоначального ответа. Discord предъявляет строгие требования ко времени подтверждения Interaction, а маршрут через два сервера Adal добавляет дополнительные сетевые переходы.

Итог

Если прямой доступ к адресам Discord закрыт политикой сети, HTTP-бота не обязательно переносить на неуправляемый компьютер или просить снять ограничения целиком.

Можно согласовать ограниченную архитектуру через Adal:

  1. Discord отправляет Interactions на публичный входящий сервер Adal.

  2. Adal CLI доставляет их локальному приложению.

  3. Приложение проверяет подпись и формирует ответ.

  4. Отдельный исходящий сервер Adal доставляет callback в Discord через Direct HTTP.

  5. История приёма и отправки остаётся доступна для диагностики и контроля.

Локальное приложение работает внутри закрытой сети, не принимает прямые подключения из интернета и не обращается к адресам Discord напрямую. При этом сетевые правила организации сохраняются, а интеграция получает понятный, ограниченный и наблюдаемый маршрут.