From d4257c83157193a4b0452e1d0f43ad59cc6564f2 Mon Sep 17 00:00:00 2001
From: "Edgar P. Burkhart" <git@edgarpierre.fr>
Date: Sun, 23 Mar 2025 10:14:03 +0100
Subject: [PATCH] Add NickBot for nickname management and integrate slash
 commands for dynamic nickname assignment

---
 botbotbot/__init__.py | 76 +++----------------------------------------
 botbotbot/nicks.py    | 73 +++++++++++++++++++++++++++++++++++++++++
 botbotbot/text.py     | 36 +++++++++++++++++++-
 3 files changed, 113 insertions(+), 72 deletions(-)
 create mode 100644 botbotbot/nicks.py

diff --git a/botbotbot/__init__.py b/botbotbot/__init__.py
index f4264d8..f3e7cc2 100644
--- a/botbotbot/__init__.py
+++ b/botbotbot/__init__.py
@@ -1,10 +1,10 @@
 import logging
-import random
 import tomllib
 
 import discord
 
 from botbotbot.ai import AIBot
+from botbotbot.nicks import NickBot
 from botbotbot.shuffle import Shuffler
 from botbotbot.text import TextBot
 from botbotbot.tts import CambAI
@@ -60,83 +60,17 @@ def main() -> None:
     wl = Wordlist(bot, guild_ids)
     wl.init_events()
 
-    text_bot = TextBot(bot, wl, aibot=aibot, shuffler=shf)
+    text_bot = TextBot(bot, wl, aibot=aibot, shuffler=shf, guild_ids=guild_ids)
     text_bot.init_events()
 
     voice_bot = VoiceBot(bot, cambai, aibot=aibot, shuffler=shf, guild_ids=guild_ids)
     voice_bot.init_events()
 
+    nick_bot = NickBot(bot, shuffler=shf, guild_ids=guild_ids)
+    nick_bot.init_events()
+
     @bot.listen("on_ready")
     async def on_ready() -> None:
         logger.info(f"We have logged in as {bot.user}")
 
-    @bot.slash_command(
-        name="alea", guild_ids=guild_ids, description="Modifier les pseudos"
-    )
-    async def alea(ctx: discord.ApplicationContext) -> None:
-        logger.info(f"ALEA {ctx.author}")
-        await ctx.defer()
-        if await shf.try_shuffle(ctx.guild):
-            embed = discord.Embed(title="ALEA", color=discord.Colour.green())
-            await ctx.respond(embed=embed, ephemeral=True, delete_after=30)
-            logger.info("FIN ALEA")
-        else:
-            embed = discord.Embed(title="ERRE ALEA", color=discord.Colour.red())
-            await ctx.respond(embed=embed)
-            logger.info("ERRE ALEA")
-
-    @bot.slash_command(
-        name="indu", guild_ids=guild_ids, description="Poser une question à MistralAI"
-    )
-    async def indu(ctx: discord.ApplicationContext, prompt: str) -> None:
-        if aibot is None:
-            return
-        logger.info(f"INDU {ctx.author} {prompt}")
-        await ctx.defer()
-        res_stream = await aibot.get_response_stream(prompt)
-
-        embed = discord.Embed(
-            title=prompt,
-            description="",
-            thumbnail="https://mistral.ai/images/favicon/favicon-32x32.png",
-            color=discord.Colour.orange(),
-        )
-        message = await ctx.respond(embed=embed)
-
-        async for chunk in res_stream:
-            if chunk.data.choices[0].delta.content is not None:
-                embed.description += chunk.data.choices[0].delta.content
-                await message.edit(embed=embed)
-
-        embed.colour = None
-        await message.edit(embed=embed)
-        logger.info("FIN INDU")
-
-    @bot.slash_command(
-        name="chan", guild_ids=guild_ids, description="Donner de nouveaux pseudos"
-    )
-    async def chan(ctx: discord.ApplicationContext, file: discord.Attachment) -> None:
-        logger.info(f"CHAN {ctx.author}")
-        await ctx.defer()
-
-        members = ctx.guild.members
-        members.remove(ctx.guild.owner)
-
-        nicks = (await file.read()).decode().splitlines()
-        if len(nicks) < len(members):
-            embed = discord.Embed(title="ERRE CHAN", color=discord.Colour.red())
-            await ctx.respond(embed=embed)
-            return
-
-        nicks = random.choices(nicks, k=len(members))
-        for member, nick in zip(members, nicks):
-            logger.info(member, nick)
-            await member.edit(nick=nick)
-
-        embed = discord.Embed(
-            title="CHAN", description="\n".join(nicks), color=discord.Colour.green()
-        )
-        await ctx.respond(embed=embed)
-        logger.info("FIN CHAN")
-
     bot.run(config.get("token"))
diff --git a/botbotbot/nicks.py b/botbotbot/nicks.py
new file mode 100644
index 0000000..092fceb
--- /dev/null
+++ b/botbotbot/nicks.py
@@ -0,0 +1,73 @@
+import logging
+import random
+
+import discord
+
+from botbotbot.shuffle import Shuffler
+
+logger = logging.getLogger(__name__)
+
+
+class NickBot:
+    def __init__(
+        self,
+        bot: discord.Bot,
+        shuffler: Shuffler,
+        guild_ids: list[int] = [],
+    ) -> None:
+        self.bot = bot
+        self.shf = shuffler
+        self.guild_ids = guild_ids
+
+    def init_events(self) -> None:
+        self.bot.add_application_command(
+            discord.SlashCommand(
+                self.alea,
+                name="alea",
+                description="Modifier les pseudos",
+                guild_ids=self.guild_ids,
+            )
+        )
+        self.bot.add_application_command(
+            discord.SlashCommand(
+                self.chan,
+                name="chan",
+                description="Donner de nouveaux pseudos",
+                guild_ids=self.guild_ids,
+            )
+        )
+
+    async def alea(self, ctx: discord.ApplicationContext) -> None:
+        logger.info(f"ALEA <{ctx.author}>.")
+        await ctx.defer()
+        if await self.shf.try_shuffle(ctx.guild):
+            await ctx.respond("ALEA", ephemeral=True, delete_after=30)
+            logger.info("FIN ALEA")
+        else:
+            await ctx.respond("ERRE ALEA", ephemeral=True, delete_after=30)
+            logger.info("ERRE ALEA")
+
+    async def chan(
+        self, ctx: discord.ApplicationContext, file: discord.Attachment
+    ) -> None:
+        logger.info(f"CHAN <{ctx.author}>.")
+        await ctx.defer()
+
+        members = ctx.guild.members
+        members.remove(ctx.guild.owner)
+
+        nicks = (await file.read()).decode().splitlines()
+        if len(nicks) < len(members):
+            await ctx.respond("ERRE CHAN", ephemeral=True, delete_after=30)
+            return
+
+        nicks = random.choices(nicks, k=len(members))
+        for member, nick in zip(members, nicks):
+            logger.info(member, nick)
+            await member.edit(nick=nick)
+
+        embed = discord.Embed(
+            title="CHAN", description="\n".join(nicks), color=discord.Colour.green()
+        )
+        await ctx.respond(embed=embed, ephemeral=True, delete_after=30)
+        logger.info("FIN CHAN")
diff --git a/botbotbot/text.py b/botbotbot/text.py
index 50a3cd6..2342378 100644
--- a/botbotbot/text.py
+++ b/botbotbot/text.py
@@ -21,6 +21,7 @@ class TextBot:
         wordlist: Wordlist,
         aibot: AIBot | None = None,
         shuffler: Shuffler | None = None,
+        guild_ids: list[int] = [],
         rnd_weights: list[float] = [10, 5, 10],
     ) -> None:
         self.bot = bot
@@ -28,12 +29,21 @@ class TextBot:
         self.wl = wordlist
         self._rnd_weights = rnd_weights
         self.shf = shuffler
+        self.guild_ids = guild_ids
 
     def init_events(self) -> None:
         self.bot.add_listener(self.on_message, "on_message")
         self.bot.add_listener(self.add_more_reaction, "on_reaction_add")
         self.bot.add_listener(self.react_message_edit, "on_message_edit")
         self.bot.add_listener(self.rando_shuffle, "on_message")
+        self.bot.add_application_command(
+            discord.SlashCommand(
+                self.indu,
+                name="indu",
+                guild_ids=self.guild_ids,
+                description="Poser une question à MistralAI",
+            )
+        )
 
     @property
     def rnd_weights(self) -> list[float]:
@@ -167,7 +177,7 @@ class TextBot:
             and after.author != self.bot.user
             and random.random() < 20 / 100
         ):
-            logger.info(f"React to edit from {after.author}")
+            logger.info(f"React to edit from {after.author}.")
             await after.add_reaction("👀")
 
     async def rando_shuffle(self, message: discord.Message) -> None:
@@ -179,3 +189,27 @@ class TextBot:
         ):
             logger.info(f"Message shuffle after message from {message.author}")
             await self.shf.try_shuffle(message.guild)
+
+    async def indu(self, ctx: discord.ApplicationContext, prompt: str) -> None:
+        if self.aibot is None:
+            return
+        logger.info(f"INDU {ctx.author} {prompt}")
+        await ctx.defer()
+        res_stream = await self.aibot.get_response_stream(prompt)
+
+        embed = discord.Embed(
+            title=prompt,
+            description="",
+            thumbnail="https://mistral.ai/images/favicon/favicon-32x32.png",
+            color=discord.Colour.orange(),
+        )
+        message = await ctx.respond(embed=embed)
+
+        async for chunk in res_stream:
+            if chunk.data.choices[0].delta.content is not None:
+                embed.description += chunk.data.choices[0].delta.content
+                await message.edit(embed=embed)
+
+        embed.colour = None
+        await message.edit(embed=embed)
+        logger.info("FIN INDU")