Enhance moderation commands and ProtonDB integration in Discord bot

- Introduced a new slash command `/say` for sending messages in specific channels, improving moderation capabilities.
- Updated the ProtonDB command to include an alias `/pdb`, streamlining user interaction.
- Refactored command descriptions and improved error handling for better user experience.
This commit is contained in:
2026-05-03 18:05:50 +02:00
parent f901db1a7d
commit 769251df0d
3 changed files with 89 additions and 43 deletions
+4 -2
View File
@@ -27,12 +27,13 @@ from discordbot.moderation import (
moderation_ctx_ban_author,
moderation_ctx_kick_author,
moderation_ctx_timeout_author,
moderation_slash_say,
)
from discordbot.welcome import sendWelcomeMessage, sendLeaveMessage, updateInviteCache
from discordbot.patreon import checkPatreonPosts
from discordbot.youtube import checkYouTubeVideos
from discordbot.auto_rooms import on_voice_state_update_auto_rooms, on_raw_reaction_add_auto_rooms, on_message_auto_rooms, cleanup_orphaned_auto_rooms
from discordbot.protondb_discord import protondb_slash_command, protondb_message_context_menu
from discordbot.protondb_discord import protondb_slash_command, pdb_slash_command
class DiscordBot(discord.Client):
def __init__(self, *, intents: discord.Intents):
@@ -49,8 +50,9 @@ class DiscordBot(discord.Client):
moderation_ctx_ban_author,
moderation_ctx_kick_author,
moderation_ctx_timeout_author,
moderation_slash_say,
protondb_slash_command,
protondb_message_context_menu,
pdb_slash_command,
):
self.tree.add_command(cmd)
logging.info("Commandes d'application (transfert, modération, ProtonDB) ajoutées au CommandTree")
+55 -3
View File
@@ -910,9 +910,8 @@ async def handle_staff_help_command(message: Message, bot):
if ConfigurationHelper().getValue('proton_db_enable_enable'):
public_commands.append(
"**🎮 ProtonDB**\n"
"• `/protondb` — recherche par nom de jeu\n"
"• Clic droit sur un message → Applications → **Rechercher sur ProtonDB**\n"
"Ex. `/protondb` avec le paramètre *jeu* : Elden Ring"
"• `/protondb` ou `/pdb` — recherche par nom de jeu\n"
"Ex. `/pdb` avec le paramètre *jeu* : Elden Ring"
)
from database.models import Commande
@@ -998,6 +997,7 @@ async def handle_staff_help_command(message: Message, bot):
embed.add_field(
name="💬 Autres",
value=(
"• `/say` — salon + message (équivalent de `!say`)\n"
"• `!say #channel message`\n"
" Envoie un message en tant que bot\n"
" Ex: `!say #annonces Nouvelle fonctionnalité !`\n\n"
@@ -1979,6 +1979,58 @@ async def moderation_slash_timeout(
ephemeral=True,
)
@app_commands.command(name="say", description="Envoie un message dans un salon ou un fil (équivalent de !say).")
@app_commands.guild_only()
@app_commands.default_permissions(manage_messages=True)
@app_commands.describe(canal="Salon ou fil cible", message="Contenu du message à envoyer")
async def moderation_slash_say(
interaction: discord.Interaction,
canal: discord.TextChannel | discord.Thread,
message: str,
):
if not _interaction_must_be_staff_member(interaction):
await interaction.response.send_message(
"❌ Vous n'avez pas les permissions nécessaires pour utiliser cette commande.",
ephemeral=True,
)
return
if interaction.guild and getattr(canal, "guild", None) and canal.guild.id != interaction.guild.id:
await interaction.response.send_message(
"❌ Vous ne pouvez cibler qu'un salon de ce serveur.",
ephemeral=True,
)
return
text = message.strip()
if not text:
await interaction.response.send_message("❌ Le message ne peut pas être vide.", ephemeral=True)
return
if len(text) > 2000:
await interaction.response.send_message(
"❌ Le message dépasse la limite de 2000 caractères.",
ephemeral=True,
)
return
try:
await canal.send(text)
except discord.Forbidden:
await interaction.response.send_message(
"❌ Je n'ai pas les permissions pour écrire dans ce canal.",
ephemeral=True,
)
return
except Exception as e:
logging.error(f"Slash say : {e}")
await interaction.response.send_message(
f"❌ Impossible d'envoyer le message : {e}",
ephemeral=True,
)
return
try:
mention = canal.mention
except Exception:
mention = f"#{canal.name}"
await interaction.response.send_message(f"✅ Message envoyé dans {mention}.", ephemeral=True)
@app_commands.context_menu(name="Bannir l'auteur")
@app_commands.guild_only()
@app_commands.default_permissions(ban_members=True)
+30 -38
View File
@@ -61,42 +61,40 @@ def _build_protondb_embed(games: List[Any]) -> discord.Embed:
async def _protondb_search_followup(interaction: discord.Interaction, query: str) -> None:
# Une seule réponse éditée (pas de followups en chaîne) : évite les fils « message introuvable »
# et supprime le besoin dun message séparé « Recherche en cours… ».
await interaction.response.defer()
search_msg = None
try:
search_msg = await interaction.followup.send(f"🔍 Recherche en cours pour **{query}**...", wait=True)
except Exception as e:
logging.error(f"ProtonDB : message de recherche : {e}")
try:
games = searhProtonDb(query)
except Exception as e:
logging.error(f"ProtonDB : searhProtonDb : {e}")
games = []
if search_msg:
try:
await search_msg.delete()
except Exception:
pass
if len(games) == 0:
await interaction.followup.send(
f"{interaction.user.mention} Je n'ai pas trouvé de jeux correspondant à **{query}**. Es-tu sûr que le jeu est disponible sur Steam ?",
suppress_embeds=True,
)
try:
await interaction.edit_original_response(
content=(
f"{interaction.user.mention} Je n'ai pas trouvé de jeux correspondant à **{query}**. "
"Es-tu sûr que le jeu est disponible sur Steam ?"
),
embed=None,
)
except Exception as e:
logging.error(f"ProtonDB : edit_original_response (vide) : {e}")
return
embed = _build_protondb_embed(games)
try:
await interaction.followup.send(embed=embed)
await interaction.edit_original_response(content=None, embed=embed)
except Exception as e:
logging.error(f"ProtonDB : envoi embed : {e}")
logging.error(f"ProtonDB : edit_original_response (embed) : {e}")
try:
await interaction.followup.send(embed=embed)
except Exception as e2:
logging.error(f"ProtonDB : followup de secours : {e2}")
@app_commands.command(name="protondb", description="Recherche un jeu sur ProtonDB (compatibilité Linux / Steam).")
@app_commands.describe(jeu="Nom du jeu (ex. Elden Ring)")
async def protondb_slash_command(interaction: discord.Interaction, jeu: str):
async def _protondb_slash_impl(interaction: discord.Interaction, jeu: str, exemple: str) -> None:
if not ConfigurationHelper().getValue('proton_db_enable_enable'):
await interaction.response.send_message(
"❌ La commande ProtonDB n'est pas activée.",
@@ -106,26 +104,20 @@ async def protondb_slash_command(interaction: discord.Interaction, jeu: str):
query = jeu.strip()
if not query:
await interaction.response.send_message(
"⚠️ Indique le nom d'un jeu.\nExemple : `/protondb jeu:Elden Ring`",
f"⚠️ Indique le nom d'un jeu.\nExemple : `{exemple}`",
ephemeral=True,
)
return
await _protondb_search_followup(interaction, query)
@app_commands.context_menu(name="Rechercher sur ProtonDB")
async def protondb_message_context_menu(interaction: discord.Interaction, message: discord.Message):
if not ConfigurationHelper().getValue('proton_db_enable_enable'):
await interaction.response.send_message(
"❌ La commande ProtonDB n'est pas activée.",
ephemeral=True,
)
return
query = (message.clean_content or "").strip()
if not query:
await interaction.response.send_message(
"❌ Ce message n'a pas de texte exploitable pour une recherche (ou seulement des pièces jointes / mentions vides).",
ephemeral=True,
)
return
await _protondb_search_followup(interaction, query)
@app_commands.command(name="protondb", description="Recherche un jeu sur ProtonDB (compatibilité Linux / Steam).")
@app_commands.describe(jeu="Nom du jeu (ex. Elden Ring)")
async def protondb_slash_command(interaction: discord.Interaction, jeu: str):
await _protondb_slash_impl(interaction, jeu, "/protondb jeu:Elden Ring")
@app_commands.command(name="pdb", description="Alias de /protondb — recherche un jeu sur ProtonDB.")
@app_commands.describe(jeu="Nom du jeu (ex. Elden Ring)")
async def pdb_slash_command(interaction: discord.Interaction, jeu: str):
await _protondb_slash_impl(interaction, jeu, "/pdb jeu:Elden Ring")