Bläddra i källkod

PRESENCE_WEIGHTS : éléments vides, doublons et jeu sans clé Steam

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Johnounet 18 timmar sedan
förälder
incheckning
ee8efe406d
3 ändrade filer med 36 tillägg och 0 borttagningar
  1. 2 0
      CHANGELOG.md
  2. 12 0
      bot/config.py
  3. 22 0
      tests/test_config.py

+ 2 - 0
CHANGELOG.md

@@ -28,6 +28,8 @@ Les versions les plus récentes sont en haut. Chaque entrée commence par un ré
 
 - **Les autres réglages ont aussi un plafond.** Une valeur trop grande n'était découverte qu'au premier message, avec un « Je suis mal configuré » sans explication (`MAX_TOKENS=200000`, `PDF_MAX_PAGES=300` alors que l'API refuse les PDF de plus de 100 pages), ou passait sans erreur mais coûtait cher (`WEB_MAX_USES=50`) ou saturait Discord (`PRESENCE_SPEED=100000`). Le bot refuse maintenant de démarrer avec un message « Erreur de configuration » clair au-delà de 64000 pour `MAX_TOKENS`, 10 pour `WEB_MAX_USES`, 20 pour `MEMORY_MAX_OPERATIONS`, 100 pour `PDF_MAX_PAGES` et 100 pour `PRESENCE_SPEED`. Les bornes sont indiquées dans `.env.exemple`.
 
+- **`PRESENCE_WEIGHTS` mieux vérifié.** Une virgule finale ou doublée ne donne plus « Statut inconnu dans PRESENCE_WEIGHTS : '' » : l'élément vide est ignoré. Un statut écrit deux fois n'est plus accepté en silence (la dernière valeur gagnait) : le bot refuse de démarrer. Enfin, si `jeu` est le seul statut possible sans `STEAM_API_KEY`, le bot ne reste plus « en ligne » sans activité ni avertissement : il refuse de démarrer avec un message « Erreur de configuration » clair.
+
 - **Un dossier absent est signalé au démarrage.** Si le dossier de `LOG_FILE`, de `HISTORY_FILE` ou de `MEMORY_DIR` n'existe pas (volume Docker mal monté, faute de frappe) ou n'est pas accessible en écriture, le bot refuse de démarrer avec un message « Erreur de configuration » clair, au lieu d'une trace Python (`LOG_FILE`) ou de tourner sans jamais rien sauvegarder (`HISTORY_FILE`). L'historique est aussi enregistré comme les autres fichiers : envoyé jusqu'au disque avant de remplacer l'ancien, pour ne pas finir vide après une coupure de courant.
 
 ---

+ 12 - 0
bot/config.py

@@ -95,11 +95,17 @@ def _get_presence_weights() -> dict[str, int]:
     raw = _get_str("PRESENCE_WEIGHTS")
     if raw is None:
         return weights
+    seen = set()
     for item in raw.split(","):
+        if not item.strip():
+            continue  # virgule finale ou doublée
         name, _, value = item.partition("=")
         name = name.strip().lower()
         if name not in weights:
             raise ConfigError(f"Statut inconnu dans PRESENCE_WEIGHTS : {name!r} (statuts possibles : {', '.join(weights)}).")
+        if name in seen:
+            raise ConfigError(f"PRESENCE_WEIGHTS : le statut {name} apparaît plusieurs fois.")
+        seen.add(name)
         try:
             weights[name] = int(value)
         except ValueError:
@@ -291,6 +297,12 @@ def load_settings() -> Settings:
             _check_output("MEMORY_DIR", settings.memory_dir, folder_only=True)
         else:
             _check_output("MEMORY_DIR", settings.memory_dir)
+    # Sans clé Steam, « jeu » est retiré au tirage : s'il était le seul statut possible, le bot resterait
+    # « en ligne » sans activité.
+    if settings.presence and not settings.steam_api_key and not any(
+            weight for name, weight in settings.presence_weights.items() if name != "jeu"):
+        raise ConfigError("PRESENCE_WEIGHTS : le statut jeu est le seul possible, mais il nécessite STEAM_API_KEY. "
+                          "Renseigne STEAM_API_KEY ou donne une valeur supérieure à 0 à un autre statut.")
     if settings.memory and settings.max_tokens < (minimum := memory_max_tokens(settings)[0]):
         raise ConfigError(f"MAX_TOKENS ({settings.max_tokens}) est trop petit pour la mémoire des membres : la "
                           f"réécriture d'un fichier plein serait coupée et la note perdue. Mets au moins {minimum}, "

+ 22 - 0
tests/test_config.py

@@ -92,5 +92,27 @@ class MaximumTest(unittest.TestCase):
         self.assertEqual((s.max_tokens, s.pdf_max_pages, s.presence_speed), (64000, 100, 100))
 
 
+class PresenceWeightsTest(unittest.TestCase):
+    ONLY_GAME = "anime=0,musique=0,pensee=0,jeu=100,en_ligne=0,ne_pas_deranger=0,absent=0"
+
+    def test_empty_items_ignored(self):
+        s = settings(MEMORY="off", PRESENCE_WEIGHTS="anime=5,,musique=7,")
+        self.assertEqual((s.presence_weights["anime"], s.presence_weights["musique"]), (5, 7))
+
+    def test_duplicate_refused(self):
+        with self.assertRaises(ConfigError) as ctx:
+            settings(MEMORY="off", PRESENCE_WEIGHTS="anime=5,Anime=0")
+        self.assertIn("le statut anime apparaît plusieurs fois", str(ctx.exception))
+
+    def test_only_game_without_steam_key_refused(self):
+        with self.assertRaises(ConfigError) as ctx:
+            settings(MEMORY="off", PRESENCE_WEIGHTS=self.ONLY_GAME)
+        self.assertIn("nécessite STEAM_API_KEY", str(ctx.exception))
+
+    def test_only_game_accepted_with_steam_key_or_presence_off(self):
+        settings(MEMORY="off", PRESENCE_WEIGHTS=self.ONLY_GAME, STEAM_API_KEY="x")
+        settings(MEMORY="off", PRESENCE_WEIGHTS=self.ONLY_GAME, PRESENCE="off")
+
+
 if __name__ == "__main__":
     unittest.main()