Pārlūkot izejas kodu

Découpage des messages : plus de bloc de code vide en fin de morceau

Quand la ligne d'ouverture d'un bloc de code (« ```py ») remplissait le
morceau en cours à 3 caractères près, la ligne suivante déclenchait
flush(), qui ajoutait « \n``` » : le morceau dépassait la limite (jusqu'à
limit + 3) et se terminait par un bloc vide « ```py\n``` ». La place
était vérifiée avant la mise à jour de open_fence, donc sans compter la
fermeture à venir. Discord refusait ce morceau et send_answer s'arrêtait
là : la suite de la réponse n'était pas envoyée. Même risque pour les
fiches de /memoire (EMBED_MAX).

Une ligne qui ouvre un bloc doit maintenant laisser la place de le
refermer, et un morceau qui finirait sur un bloc encore vide laisse sa
ligne d'ouverture au morceau suivant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Johnounet 6 stundas atpakaļ
vecāks
revīzija
6e3a80c0de
3 mainītis faili ar 14 papildinājumiem un 7 dzēšanām
  1. 2 0
      CHANGELOG.md
  2. 10 3
      bot/text_utils.py
  3. 2 4
      tests/test_text_utils.py

+ 2 - 0
CHANGELOG.md

@@ -52,6 +52,8 @@ Les versions les plus récentes sont en haut. Chaque entrée commence par un ré
 
 - **Une clé Anthropic ou un modèle faux arrête le bot.** Une `ANTHROPIC_API_KEY` ou un `CLAUDE_MODEL` invalide ne se voyait qu'au premier message, avec « Je suis mal configuré ». Le bot les vérifie maintenant au démarrage ; seul un crédit épuisé ne se voit toujours qu'au premier message.
 
+- **Une longue réponse avec du code n'est plus coupée.** Quand un bloc de code commençait juste à la fin d'un morceau de 2000 caractères, ce morceau dépassait la limite de Discord et la suite de la réponse n'était pas envoyée. Le bloc passe maintenant au morceau suivant ; même chose pour les fiches de `/memoire`.
+
 ---
 
 ## Version 5.11

+ 10 - 3
bot/text_utils.py

@@ -47,20 +47,27 @@ def split_message(text: str, limit: int = DISCORD_LIMIT) -> list[str]:
         nonlocal current
         body = current.rstrip("\n")
         if open_fence is not None:
-            body += "\n```"
+            head, _, last = body.rpartition("\n")
+            if last.strip() == open_fence:
+                # Bloc ouvert en fin de morceau, encore vide : sa ligne d'ouverture passe au morceau suivant.
+                body = head
+            else:
+                body += "\n```"
         if body.strip():
             chunks.append(body)
         current = (open_fence + "\n") if open_fence is not None else ""
 
     for line in text.split("\n"):
+        match = _FENCE_RE.match(line)
+        # Une ligne qui ouvre un bloc doit laisser la place de le refermer.
+        room_to_close = len("\n```") if match and open_fence is None else closing_len()
         # Une ligne trop longue à elle seule est découpée en morceaux plus petits.
         for piece in _split_long_line(line, limit - 10 - (len(open_fence) if open_fence else 0)):
             addition = piece + "\n"
-            if len(current) + len(addition) + closing_len() > limit:
+            if len(current) + len(addition) + room_to_close > limit:
                 flush()
             current += addition
 
-        match = _FENCE_RE.match(line)
         if match:
             open_fence = None if open_fence is not None else line.strip()
 

+ 2 - 4
tests/test_text_utils.py

@@ -121,11 +121,9 @@ class SplitMessageTest(unittest.TestCase):
                 self.assertGreater(len(chunks), 1)
                 self.assert_valid(chunks, limit)
 
-    @unittest.expectedFailure
     def test_opening_fence_at_end_of_chunk(self):
-        # BUG : quand la ligne d'ouverture d'un bloc (```py) remplit le morceau à 1, 2 ou 3 caractères
-        # près, le morceau se termine par un bloc vide « ```py\n``` » qui dépasse la limite de 3 caractères
-        # au plus : Discord refuserait ce message. La ligne d'ouverture devrait passer au morceau suivant.
+        # La ligne d'ouverture d'un bloc (```py) qui remplit presque le morceau passe au morceau suivant,
+        # au lieu de laisser un bloc vide « ```py\n``` » qui pourrait dépasser la limite.
         for before in range(1990, 2000):
             with self.subTest(before=before):
                 text = "a" * before + "\n```py\n" + "b" * 10 + "\nfin\n```"