Bladeren bron

Commandes slash : synchronisées seulement quand elles changent

Les commandes slash ne sont plus renvoyées à Discord à chaque démarrage,
ce que Discord limite. Leur empreinte (avec l'identifiant de
l'application) est gardée dans slash_commands.sha256, à côté de
l'historique, et l'envoi n'a lieu que si elle change. Un échec de
l'envoi n'empêche plus le bot de démarrer : il réessaie au démarrage
suivant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Johnounet 4 dagen geleden
bovenliggende
commit
e7c3fb7083
4 gewijzigde bestanden met toevoegingen van 34 en 2 verwijderingen
  1. 1 0
      .dockerignore
  2. 1 0
      .gitignore
  3. 27 2
      bot/discord_bot.py
  4. 5 0
      changelog.txt

+ 1 - 0
.dockerignore

@@ -12,6 +12,7 @@
 **/conversation_history.json*
 **/presence_sources.json
 **/waiting_messages.json
+**/slash_commands.sha256
 **/bot.log*
 
 .venv/

+ 1 - 0
.gitignore

@@ -7,6 +7,7 @@ bot.log*
 conversation_history.json*
 presence_sources.json
 waiting_messages.json
+slash_commands.sha256
 .claude/
 .vscode/
 # Personnalité perso (peut contenir des infos privées)

+ 27 - 2
bot/discord_bot.py

@@ -4,6 +4,7 @@ from __future__ import annotations
 
 import asyncio
 import contextlib
+import hashlib
 import json
 import logging
 import random
@@ -117,14 +118,15 @@ class ChatBot(commands.Bot):
         self._waiting_file = settings.history_file.with_name("waiting_messages.json")
         self._waiting: set[tuple[int, int]] = set()  # (salon, message)
         self._last_main_message: datetime | None = None  # date du dernier message reçu dans le salon dédié
+        # Empreinte des commandes slash envoyées à Discord (voir sync_commands).
+        self._commands_file = settings.history_file.with_name("slash_commands.sha256")
 
     # ------------------------------------------------------------ cycle de vie
 
     async def setup_hook(self) -> None:
         # Exécuté une seule fois au démarrage (contrairement à on_ready).
         self.tree.add_command(reset_command)
-        synced = await self.tree.sync()
-        logger.info("%d commande(s) slash synchronisée(s).", len(synced))
+        await self.sync_commands()
         s = self.settings
         logger.info("Modèle %s — réflexion : %s, effort : %s, recherche web : %s",
                     s.model, "on" if s.thinking else "off", s.effort or "par défaut",
@@ -132,6 +134,29 @@ class ChatBot(commands.Bot):
         if self.presence:
             self.presence.start()
 
+    async def sync_commands(self) -> None:
+        """Envoie les commandes slash à Discord, seulement si elles ont changé depuis le dernier envoi
+        (Discord limite ces envois : inutile de les refaire à chaque démarrage)."""
+        payload = [command.to_dict(self.tree) for command in self.tree.get_commands()]
+        signature = json.dumps([self.application_id, payload], sort_keys=True, ensure_ascii=False)
+        digest = hashlib.sha256(signature.encode("utf-8")).hexdigest()
+        try:
+            if self._commands_file.read_text(encoding="utf-8").strip() == digest:
+                logger.info("Commandes slash inchangées : pas de synchronisation.")
+                return
+        except OSError:
+            pass  # premier démarrage, ou fichier illisible : on synchronise
+        try:
+            synced = await self.tree.sync()
+        except discord.HTTPException as exc:  # le bot fonctionne quand même ; nouvel essai au prochain démarrage
+            logger.error("Synchronisation des commandes slash impossible : %s", exc)
+            return
+        logger.info("%d commande(s) slash synchronisée(s).", len(synced))
+        try:
+            self._commands_file.write_text(digest, encoding="utf-8")
+        except OSError as exc:
+            logger.warning("Impossible d'écrire %s : %s", self._commands_file, exc)
+
     async def on_ready(self) -> None:
         logger.info("Connecté en tant que %s (version %s)", self.user, VERSION)
         if self.presence:

+ 5 - 0
changelog.txt

@@ -43,6 +43,11 @@ Corrections
 - Si Discord refuse l'indicateur « en train d'écrire » (permission
   manquante, panne passagère), la réponse est quand même envoyée, au lieu
   d'être perdue.
+- Commandes slash : elles ne sont plus renvoyées à Discord à chaque
+  démarrage (Discord limite ces envois), seulement quand elles ont changé.
+  Leur empreinte est gardée dans slash_commands.sha256, à côté de
+  l'historique (le supprimer force un nouvel envoi). Un échec de l'envoi
+  n'empêche plus le bot de démarrer : il réessaie au démarrage suivant.
 
 
 Version 5.6 — 30/10/2026