1
0

commands.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. """Commandes slash : /reset, /skip, /memoire et /memoire-serveur (enregistrées par ChatBot.setup_hook)."""
  2. from __future__ import annotations
  3. import logging
  4. from typing import TYPE_CHECKING
  5. import discord
  6. from discord import app_commands
  7. from .memory_views import MemoryCard, member_target, see_user, server_target
  8. if TYPE_CHECKING:
  9. from .discord_bot import ChatBot
  10. logger = logging.getLogger("chatbot.discord") # même nom qu'avant : le journal ne change pas
  11. async def allowed(interaction: discord.Interaction) -> bool:
  12. """Commande réservée aux administrateurs : prévient les autres et renvoie False."""
  13. # Vérification côté bot, au cas où un admin aurait ouvert la commande à tous dans les paramètres du serveur.
  14. if interaction.permissions.administrator:
  15. return True
  16. logger.info("Tentative de /%s refusée pour %s dans le salon %s",
  17. interaction.command.name if interaction.command else "?", interaction.user, interaction.channel_id)
  18. await interaction.response.send_message(
  19. "Seuls les administrateurs du serveur peuvent utiliser cette commande.",
  20. ephemeral=True,
  21. )
  22. return False
  23. @app_commands.command(name="reset", description="Réinitialise la mémoire du bot dans ce salon")
  24. @app_commands.guild_only()
  25. # Masque la commande aux membres qui n'ont pas la permission Administrateur.
  26. @app_commands.default_permissions(administrator=True)
  27. async def reset_command(interaction: discord.Interaction) -> None:
  28. if not await allowed(interaction):
  29. return
  30. bot: ChatBot = interaction.client # type: ignore[assignment]
  31. # Le reset attend la fin d'une éventuelle réponse en cours, ce qui peut dépasser
  32. # les 3 secondes accordées par Discord pour répondre à une commande.
  33. await interaction.response.defer()
  34. await bot.history.reset(interaction.channel_id)
  35. logger.info("Historique réinitialisé dans le salon %s par %s", interaction.channel_id, interaction.user)
  36. await interaction.followup.send("L'historique de conversation a été réinitialisé.")
  37. @app_commands.command(name="skip", description="Passe tout de suite au statut Discord suivant du bot")
  38. @app_commands.guild_only()
  39. @app_commands.default_permissions(administrator=True) # comme /reset
  40. async def skip_command(interaction: discord.Interaction) -> None:
  41. if not await allowed(interaction):
  42. return
  43. bot: ChatBot = interaction.client # type: ignore[assignment]
  44. # Une pensée ou une raison d'absence est écrite par Claude : plus de 3 secondes possibles.
  45. await interaction.response.defer(ephemeral=True)
  46. try:
  47. status = await bot.presence.skip()
  48. except Exception:
  49. logger.exception("Erreur inattendue pendant /skip")
  50. await interaction.followup.send("Le statut n'a pas pu être changé, regarde les logs.", ephemeral=True)
  51. return
  52. if status is None:
  53. await interaction.followup.send("C'est la nuit : le bot dort, il n'y a pas de statut à passer.", ephemeral=True)
  54. return
  55. logger.info("Statut passé par %s", interaction.user)
  56. remaining = bot.presence.remaining()
  57. duration = f", pendant environ {remaining}" if remaining else ""
  58. await interaction.followup.send(f"Nouveau statut : {status.label()}{duration}.", ephemeral=True)
  59. MEMORY_OFF = "La mémoire des membres est désactivée sur ce bot (MEMORY=off)."
  60. MEMORY_BROKEN = ("La mémoire des membres est coupée pour l'instant, à cause d'une erreur du bot. "
  61. "Préviens l'administrateur du bot.")
  62. def memory_off(bot: ChatBot) -> str:
  63. """Pourquoi /memoire est indisponible : MEMORY=off, ou registre des membres illisible."""
  64. return MEMORY_BROKEN if bot.settings.memory else MEMORY_OFF
  65. def bot_name(interaction: discord.Interaction) -> str:
  66. return interaction.guild.me.display_name if interaction.guild else interaction.client.user.name
  67. @app_commands.command(name="memoire", description="Voir, modifier ou effacer ce que le bot a retenu sur toi")
  68. @app_commands.describe(membre="Réservé aux administrateurs : la fiche d'un autre membre, ou son identifiant s'il est parti")
  69. @app_commands.guild_only()
  70. # discord.User plutôt que discord.Member : un membre parti du serveur reste accepté (Discord le résout par
  71. # son identifiant), pour que sa fiche puisse encore être effacée.
  72. async def memory_command(interaction: discord.Interaction, membre: discord.User | None = None) -> None:
  73. bot: ChatBot = interaction.client # type: ignore[assignment]
  74. if bot.memory is None:
  75. await interaction.response.send_message(memory_off(bot), ephemeral=True)
  76. return
  77. target = membre or interaction.user
  78. if target.bot:
  79. await interaction.response.send_message("Les bots n'ont pas de fiche.", ephemeral=True)
  80. return
  81. if target.id != interaction.user.id and not interaction.permissions.administrator:
  82. logger.info("/memoire sur %s refusé pour %s", target, interaction.user)
  83. await interaction.response.send_message(
  84. "Seuls les administrateurs peuvent voir la fiche d'un autre membre.", ephemeral=True)
  85. return
  86. await see_user(bot.members, target) # un membre qui n'a encore jamais écrit peut déjà activer la mémoire
  87. if target.id != interaction.user.id:
  88. # Lecture tracée, sans le contenu : un administrateur ne peut pas lire les fiches sans laisser de trace.
  89. logger.info("/memoire : fiche de %s ouverte par %s", target, interaction.user)
  90. card = MemoryCard(bot.memory, member_target(bot.memory, target, interaction.user.id),
  91. interaction.user.id, bot_name(interaction))
  92. await card.send(interaction)
  93. @app_commands.command(name="memoire-serveur", description="Voir ou modifier le fichier général du bot sur le serveur")
  94. @app_commands.guild_only()
  95. @app_commands.default_permissions(administrator=True) # comme /reset
  96. async def server_memory_command(interaction: discord.Interaction) -> None:
  97. if not await allowed(interaction):
  98. return
  99. bot: ChatBot = interaction.client # type: ignore[assignment]
  100. if bot.memory is None:
  101. await interaction.response.send_message(memory_off(bot), ephemeral=True)
  102. return
  103. await MemoryCard(bot.memory, server_target(), interaction.user.id, bot_name(interaction)).send(interaction)