Bladeren bron

Une réponse groupée aux mentions voit la précédente

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Johnounet 12 uur geleden
bovenliggende
commit
8182f8d3d4
3 gewijzigde bestanden met toevoegingen van 34 en 3 verwijderingen
  1. 1 1
      CHANGELOG.md
  2. 4 1
      bot/discord_bot.py
  3. 29 1
      tests/test_mentions.py

+ 1 - 1
CHANGELOG.md

@@ -20,7 +20,7 @@ Les versions les plus récentes sont en haut. Chaque entrée commence par un ré
 
 - **Deux bots ne tournent plus en boucle.** Rika répond aux autres bots quand ils la mentionnent ou répondent à l'un de ses messages, sans aucune limite : face à un bot qui répond à tout avec « Répondre » (et donc la pinge), chaque réponse déclenchait la suivante, avec un appel à Claude toutes les quelques secondes jusqu'à ce que quelqu'un coupe un des bots. Elle ne répond plus à plus de 5 messages de bots d'affilée dans un salon ; le compte repart de zéro au premier message d'un membre dans ce salon, ou après 10 minutes sans message de bot. Le journal le signale une fois par série.
 
-- **Une rafale de mentions ne lance plus un appel par message.** Hors du salon dédié, chaque mention lançait son propre appel à Claude, en parallèle : 10 « @Rika ? » envoyés en 5 secondes dans #général donnaient 10 appels, chacun avec ses 20 messages de contexte, puis 10 réponses à la suite. Les mentions fonctionnent maintenant comme le salon dédié : une seule réponse à la fois par salon, et les mentions arrivées pendant qu'elle écrit partent ensemble dans l'appel suivant, avec leurs pièces jointes (deux appels au lieu de dix). Si une pièce jointe est refusée, seule cette mention est retirée. Les mentions reçues pendant une absence reçoivent aussi une seule réponse au lieu d'une par message.
+- **Une rafale de mentions ne lance plus un appel par message.** Hors du salon dédié, chaque mention lançait son propre appel à Claude, en parallèle : 10 « @Rika ? » envoyés en 5 secondes dans #général donnaient 10 appels, chacun avec ses 20 messages de contexte, puis 10 réponses à la suite. Les mentions fonctionnent maintenant comme le salon dédié : une seule réponse à la fois par salon, et les mentions arrivées pendant qu'elle écrit partent ensemble dans l'appel suivant, avec leurs pièces jointes (deux appels au lieu de dix). La réponse groupée relit aussi celle que Rika vient de poster : elle ne se répète pas et ne se contredit pas. Si une pièce jointe est refusée, seule cette mention est retirée. Les mentions reçues pendant une absence reçoivent aussi une seule réponse au lieu d'une par message.
 
 - **Erreurs de démarrage : toutes d'un coup, et dans `bot.log`.** Le bot s'arrêtait à la première variable fausse du `.env` : avec trois erreurs, il fallait trois redémarrages. Il les liste maintenant toutes. Le message n'était écrit que sur la console : il va aussi dans `bot.log` quand `LOG_FILE` est utilisable, pour qu'un redémarrage raté y laisse une trace. Enfin, un PC hors ligne ou une panne de Discord au démarrage donnaient une longue trace technique ; le bot dit maintenant « Impossible de joindre Discord : vérifie la connexion Internet » ou « Discord semble en panne ».
 

+ 4 - 1
bot/discord_bot.py

@@ -575,9 +575,12 @@ class ChatBot(commands.Bot):
         channel = mentions[-1].channel
         mention_ids = {m.id for m in mentions}
         lines: list[str] = []
+        # Jusqu'à maintenant : une réponse groupée voit celle que le bot vient d'envoyer (postée après ces
+        # mentions). En retard, jusqu'à la dernière mention : la discussion qui a suivi recouvrirait celle-ci.
+        before = mentions[-1] if late else None
         if self.settings.context_message_limit > 0:
             try:
-                async for msg in channel.history(limit=self.settings.context_message_limit, before=mentions[-1]):
+                async for msg in channel.history(limit=self.settings.context_message_limit, before=before):
                     if msg.id in mention_ids:
                         continue  # décrite plus bas, avec ses pièces jointes
                     content = short_emojis(msg.clean_content.strip())

+ 29 - 1
tests/test_mentions.py

@@ -46,7 +46,7 @@ def fake_channel(history: list) -> mock.Mock:
     channel.name = "général"
 
     async def read(limit, before):
-        for msg in reversed([m for m in history if m.id < before.id][-limit:]):
+        for msg in reversed([m for m in history if before is None or m.id < before.id][-limit:]):
             yield msg
     channel.history = read
     return channel
@@ -100,6 +100,34 @@ class MentionTest(unittest.IsolatedAsyncioTestCase):
         bot.member_notes.assert_awaited_once_with([history[1], history[3]])
         bot.send_answer.assert_awaited_once_with(history[3], "Coucou", reply=False)
 
+    async def test_grouped_answer_sees_previous_answer(self):
+        # La réponse à la 1re mention est postée après les mentions regroupées : elle doit être dans le contexte.
+        bot = fake_bot()
+        history = []
+        channel = fake_channel(history)
+        history += [fake_mention(1, "tu fais quoi ce soir ?", channel), fake_mention(2, "?", channel),
+                    fake_mention(3, "réponds !", channel),
+                    mock.Mock(id=4, clean_content="Je regarde un film !", attachments=[], stickers=[])]
+        history[3].author.display_name = history[3].author.name = "Rika"
+        await ChatBot.answer_mention(bot, history[1:3])
+        self.assertIn("Rika : Je regarde un film !", bot.claude.ask.await_args.args[1][0][0])
+
+    async def test_late_context_stops_at_last_mention(self):
+        # En retard, la discussion qui a suivi les mentions ne remplace pas celle qui les entoure.
+        bot = fake_bot()
+        history = []
+        channel = fake_channel(history)
+        history += [mock.Mock(id=1, clean_content="avant", attachments=[], stickers=[]),
+                    fake_mention(2, "tu es là ?", channel),
+                    mock.Mock(id=3, clean_content="après", attachments=[], stickers=[])]
+        for msg in history:
+            msg.author.display_name = msg.author.name = f"membre{msg.id}"
+        bot.presence.late_note.return_value = "(en retard)"
+        await ChatBot.answer_mention(bot, [history[1]], late=mock.Mock())
+        context = bot.claude.ask.await_args.args[1][0][0]
+        self.assertIn("avant", context)
+        self.assertNotIn("après", context)
+
     async def test_refused_attachment_drops_only_that_mention(self):
         bot = fake_bot()
         channel = fake_channel([])