Guide · SaaS & dev

Bot Discord : interactions HTTP, puis Docker

Premier bot Discord officiel, puis guide Node.js Docker. Deux tutoriels distincts. Endpoint /interactions. Commandes non rejouées le 2026-10-18.

Discord n’a pas, dans les pages relues le 2026-10-18, un tutoriel « mettez ce bot dans Docker ». Il a un premier bot HTTP, Building your first Discord Bot. Docker a un guide Node.js, Node.js language-specific guide, dont l’exemple est une API Express qui répond {"message":"Hello World"}, pas le dépôt d’exemple Discord.

Cet article suit le bot jusqu’à l’URL d’interactions, puis dit ce que le guide Docker couvre vraiment. Il ne fusionne pas les deux dépôts. Les commandes n’ont pas été rejouées. Pas de jeton, pas de capture. Le guide Discord en montre. Ici, non.

Architecture d’un bot Discord IA et Claude Code pour un bot Discord parlent de structure et de commandes, pas de cette image. Déployer un agent IA avec Docker couvre l’utilisateur non privilégié, les secrets et les volumes. Ne le recopiez pas : appliquez-le au conteneur une fois le CMD aligné sur le script du dépôt Discord. Docker Compose pour un SaaS couvre réseaux et volumes si vous ajoutez une base. Le premier bot officiel n’en a pas.

Étape 0, dépôt d’exemple

Node.js installé. Puis, dans l’ordre de la page :

git clone https://github.com/discord/discord-example-app.git
cd discord-example-app
npm install

La page suppose un peu de JavaScript. L’app d’exemple joue à une variante de pierre-feuille-ciseaux. Nous n’avons pas cloné le dépôt, donc nous ne listons pas ses fichiers au-delà de ceux que la page nomme : .env.sample, commands.js, app.js, utils.js, game.js.

Étape 1, l’app dans le portail

Créer l’app depuis le lien de la page, lui donner un nom, Create. La page General Information montre l’Application ID et le champ Interactions Endpoint URL. L’URL vient plus tard. La laisser vide à cette étape est ce que le guide fait.

Renommer .env.sample en .env. Ne collez aucune valeur secrète ici. Le fichier attend trois valeurs :

  • Application ID, page General Information, à la place de <YOUR_APP_ID>
  • Public Key, même page, à la place de <YOUR_PUBLIC_KEY>
  • Bot token : page Bot, Reset Token, à la place de <YOUR_BOT_TOKEN>

Le jeton ne va pas dans Git, ni dans une image, ni dans un log. Le guide Déployer un agent IA avec Docker dit comment injecter un secret au runtime. Le fichier .env du tutoriel Discord est un fichier local. Ne le COPY pas.

La page Bot permet les privileged intents et l’install par d’autres utilisateurs. Le guide dit de ne rien changer ici pour ce premier bot.

Installation : cocher User Install et Guild Install. Install Link : Discord Provided Link. Dans Default Install Settings, la page coche applications.commands pour l’install utilisateur. Pour l’install serveur, applications.commands et bot, puis la permission Send Messages dans l’exemple de l’auteur. Ce n’est pas la liste minimale universelle. C’est ce que cette page coche pour la suite.

Coller le lien d’install. Add to server sur un serveur de test, puis le même lien, Add to my apps. La page dit que le bot apparaît dans la liste des membres, et qu’on peut ouvrir un DM avec l’app installée sur le compte. Nous n’avons pas de serveur de test.

Étape 2, enregistrer les commandes

npm run register

Le script pousse ALL_COMMANDS, en bas de commands.js, en commandes globales, via PUT /applications/<APP_ID>/commands. La page dit que les commandes apparaissent, et que les lancer ne fait rien tant que l’app ne reçoit pas les requêtes.

Résultat attendu, d’après la page : les commandes visibles, sans réponse. Non vérifié ici.

Étape 3, endpoint public

npm run start

La page attend un processus sur le port 3000. Express vérifie les en-têtes et répond aux PING. Puis, dans un autre terminal, ngrok installé depuis sa page de téléchargement :

ngrok http 3000

Prendre l’URL Forwarding, pas l’interface locale 127.0.0.1:4040. Dans General Information, Interactions Endpoint URL : cette URL plus /interactions. Save Changes. La vérification, dans l’exemple, passe par PUBLIC_KEY et le paquet discord-interactions, et par une réponse aux PING.

Interactions Overview précise le contrat, et il est plus strict que le résumé du quickstart :

  • Deux livraisons s’excluent. Par défaut, la Gateway. L’URL d’interactions est un opt-in HTTP. Si vous remplissez l’URL, vous n’êtes plus sur la Gateway pour ces interactions.
  • Au moment d’enregistrer l’URL, Discord envoie un POST de type 1 (PING). Il faut répondre 200 avec un corps de type 1, et un Content-Type valide.
  • Chaque requête porte X-Signature-Ed25519 et X-Signature-Timestamp. Signature invalide : 401. Le corps vérifié est le corps brut.
  • Discord renvoie aussi des signatures invalides, exprès. Si l’endpoint les accepte, Discord retire l’URL et prévient par e-mail et DM système.

Le quickstart s’arrête à ngrok. Une URL ngrok de session n’est pas une URL de prod. Quand le tunnel tombe, l’endpoint ne vérifie plus.

La page dit ensuite de déclencher /test. La réponse attendue est un message du canal, avec « hello world » et un emoji tiré d’une fonction du dépôt. Puis /challenge, défini comme CHALLENGE_COMMAND dans commands.js, avec des options tirées de RPSChoices dans game.js. Nous n’avons pas lancé la commande.

Docker, sans prétendre que c’est le même exemple

Le guide Node.js de Docker, relu le 2026-10-18, demande Docker Desktop, un peu de JavaScript et de TypeScript, et les notions d’image et de Dockerfile. Son exemple à lui se crée dans nodejs-docker-example. Ce n’est pas discord-example-app.

Ordre de sa section « Containerize » :

  1. Fichiers de l’app d’exemple, ou le script d’échafaudage de la page. src/index.ts écoute process.env.PORT ou 3000.
  2. Si Node est installé : npm install, npm run dev, ou npm run build puis npm start. Sinon, la page dit de sauter : la suite tourne dans le conteneur.
  3. docker login dhi.io, pour tirer les images Node durcies listées dans le catalogue.
  4. Dockerfile en trois étapes : builder (dépendances et tsc), deps (prod seulement), runner (copie de dist et de node_modules, utilisateur node, EXPOSE 3000, CMD ["node", "dist/index.js"]).
  5. compose.yaml : service server, build du dossier courant, port 3000:3000.
  6. .dockerignore, pour laisser secrets et caches hors du contexte. Le .gitignore de l’exemple Docker ignore déjà .env.
docker compose up --build

Résultat attendu pour l’exemple Docker, pas pour Discord : http://localhost:3000 renvoie le JSON de salutation. Arrêt : ctrl+c, ou docker compose down si vous avez lancé avec -d.

Le dépôt Discord, d’après son quickstart, se lance avec npm run start et écoute 3000. Nous n’avons pas ouvert son package.json. Ne lui collez pas node dist/index.js parce que c’est le CMD de l’autre guide. Ouvrez le script start du dépôt cloné, et faites correspondre le CMD à ce script. Si le dépôt n’est pas en TypeScript, les étapes tsc et dist/ du guide Docker ne s’appliquent pas telles quelles.

Le quickstart Discord utilise ngrok devant un processus local. Un conteneur qui n’écoute que dans le réseau Docker ne reçoit pas les POST de Discord tant qu’une URL HTTPS publique ne pointe pas vers le port publié, et tant que cette URL plus /interactions n’est pas enregistrée. Le guide Docker ne crée pas cette URL. N’en inventez pas une.

Héberger n8n sur un VPS sépare l’installation d’un outil et son exposition publique. Ce n’est pas un bot Discord, et ce n’est pas ce Dockerfile. Si vous sortez de ngrok vers une machine, lisez ce guide pour la frontière install / exposition, pas pour les commandes n8n.

Une app qui reste connectée à la Gateway est un autre processus que l’Express du quickstart. L’overview le dit : Gateway ou URL HTTP, pas les deux. Cet article ne fournit pas de Dockerfile de Gateway. Les intents privilégiés, que le quickstart laisse de côté, sont documentés sur la page Bot. Ils ne s’activent pas en copiant l’exemple.

Sources

Sources recontrôlées le 2026-10-19. Commandes non rejouées. Pas de jeton dans cet article. Si le quickstart change le port ou le chemin /interactions, c’est la page qui compte.