Implement ProtonDB command integration in Discord bot
- Replaced the deprecated ProtonDB command handling with a new slash command and context menu for improved user experience. - Updated moderation command descriptions to reflect the new ProtonDB functionalities. - Introduced a new module for ProtonDB interactions, enhancing the bot's capability to search and display game compatibility information.
This commit is contained in:
+4
-99
@@ -32,7 +32,7 @@ from discordbot.welcome import sendWelcomeMessage, sendLeaveMessage, updateInvit
|
|||||||
from discordbot.patreon import checkPatreonPosts
|
from discordbot.patreon import checkPatreonPosts
|
||||||
from discordbot.youtube import checkYouTubeVideos
|
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.auto_rooms import on_voice_state_update_auto_rooms, on_raw_reaction_add_auto_rooms, on_message_auto_rooms, cleanup_orphaned_auto_rooms
|
||||||
from protondb import searhProtonDb
|
from discordbot.protondb_discord import protondb_slash_command, protondb_message_context_menu
|
||||||
|
|
||||||
class DiscordBot(discord.Client):
|
class DiscordBot(discord.Client):
|
||||||
def __init__(self, *, intents: discord.Intents):
|
def __init__(self, *, intents: discord.Intents):
|
||||||
@@ -49,9 +49,11 @@ class DiscordBot(discord.Client):
|
|||||||
moderation_ctx_ban_author,
|
moderation_ctx_ban_author,
|
||||||
moderation_ctx_kick_author,
|
moderation_ctx_kick_author,
|
||||||
moderation_ctx_timeout_author,
|
moderation_ctx_timeout_author,
|
||||||
|
protondb_slash_command,
|
||||||
|
protondb_message_context_menu,
|
||||||
):
|
):
|
||||||
self.tree.add_command(cmd)
|
self.tree.add_command(cmd)
|
||||||
logging.info("Commandes d'application (transfert, ban, kick, timeout) ajoutées au CommandTree")
|
logging.info("Commandes d'application (transfert, modération, ProtonDB) ajoutées au CommandTree")
|
||||||
|
|
||||||
async def on_ready(self):
|
async def on_ready(self):
|
||||||
logging.info(f'Connecté en tant que {self.user} (ID: {self.user.id})')
|
logging.info(f'Connecté en tant que {self.user} (ID: {self.user.id})')
|
||||||
@@ -230,103 +232,6 @@ async def on_message(message: Message):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f'Échec de l\'exécution de la commande Discord : {e}')
|
logging.error(f'Échec de l\'exécution de la commande Discord : {e}')
|
||||||
|
|
||||||
if (ConfigurationHelper().getValue('proton_db_enable_enable') and (message.content.startswith('!protondb') or message.content.startswith('!pdb'))):
|
|
||||||
if (message.content.find('<@')>0) :
|
|
||||||
mention = message.content[message.content.find('<@'):]
|
|
||||||
else :
|
|
||||||
mention = message.author.mention
|
|
||||||
name = message.content
|
|
||||||
if name.startswith('!protondb'):
|
|
||||||
name = name.replace('!protondb', '', 1)
|
|
||||||
elif name.startswith('!pdb'):
|
|
||||||
name = name.replace('!pdb', '', 1)
|
|
||||||
name = name.replace(f'{mention}', '').strip();
|
|
||||||
|
|
||||||
if not name or len(name) == 0:
|
|
||||||
try:
|
|
||||||
await message.delete()
|
|
||||||
delete_time = ConfigurationHelper().getIntValue('proton_db_delete_time') or 10
|
|
||||||
help_msg = await message.channel.send(
|
|
||||||
f"{mention} ⚠️ Utilisation: `!pdb nom du jeu` ou `!protondb nom du jeu`\n"
|
|
||||||
f"Exemple: `!pdb Elden Ring`",
|
|
||||||
suppress_embeds=True
|
|
||||||
)
|
|
||||||
await asyncio.sleep(delete_time)
|
|
||||||
await help_msg.delete()
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Échec de la gestion du message d'aide ProtonDB : {e}")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
searching_msg = await message.channel.send(f"🔍 Recherche en cours pour **{name}**...")
|
|
||||||
games = searhProtonDb(name)
|
|
||||||
await searching_msg.delete()
|
|
||||||
except:
|
|
||||||
games = searhProtonDb(name)
|
|
||||||
|
|
||||||
if (len(games)==0) :
|
|
||||||
msg = f'{mention} Je n\'ai pas trouvé de jeux correspondant à **{name}**. Es-tu sûr que le jeu est disponible sur Steam ?'
|
|
||||||
try:
|
|
||||||
await message.channel.send(msg, suppress_embeds=True)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Échec de l'envoi du message ProtonDB : {e}")
|
|
||||||
return
|
|
||||||
total_games = len(games)
|
|
||||||
tier_colors = {'platinum': '🟣', 'gold': '🟡', 'silver': '⚪', 'bronze': '🟤', 'borked': '🔴'}
|
|
||||||
content = ""
|
|
||||||
max_games = 15
|
|
||||||
|
|
||||||
for count, game in enumerate(games[:max_games]):
|
|
||||||
g_name = str(game.get('name'))
|
|
||||||
g_id = str(game.get('id'))
|
|
||||||
tier = str(game.get('tier') or 'N/A').lower()
|
|
||||||
tier_icon = tier_colors.get(tier, '⚫')
|
|
||||||
|
|
||||||
new_entry = f"**[{g_name}](<https://www.protondb.com/app/{g_id}>)**\n{tier_icon} Classé **{tier.capitalize()}**"
|
|
||||||
|
|
||||||
ac_status = game.get('anticheat_status')
|
|
||||||
if ac_status:
|
|
||||||
status_lower = str(ac_status).lower()
|
|
||||||
ac_map = {
|
|
||||||
'supported': ('✅', 'Supporté'),
|
|
||||||
'running': ('⚠️', 'Fonctionne'),
|
|
||||||
'broken': ('❌', 'Cassé'),
|
|
||||||
'denied': ('🚫', 'Refusé'),
|
|
||||||
'planned': ('📅', 'Planifié')
|
|
||||||
}
|
|
||||||
ac_emoji, ac_label = ac_map.get(status_lower, ('❔', str(ac_status)))
|
|
||||||
acs = game.get('anticheats') or []
|
|
||||||
ac_list = ', '.join([str(ac) for ac in acs if ac])
|
|
||||||
new_entry += f" • [Anti-cheat {ac_emoji} {ac_label}"
|
|
||||||
if ac_list:
|
|
||||||
new_entry += f" ({ac_list})"
|
|
||||||
new_entry += f"](<https://areweanticheatyet.com/game/{g_id}>)"
|
|
||||||
|
|
||||||
new_entry += "\n\n"
|
|
||||||
|
|
||||||
# Vérifier la limite avant d'ajouter
|
|
||||||
if len(content) + len(new_entry) > 3900:
|
|
||||||
rest = len(games) - count
|
|
||||||
content += f"*... et {rest} autre{'s' if rest > 1 else ''} jeu{'x' if rest > 1 else ''}*"
|
|
||||||
break
|
|
||||||
|
|
||||||
content += new_entry
|
|
||||||
else:
|
|
||||||
rest = max(0, len(games) - max_games)
|
|
||||||
if rest > 0:
|
|
||||||
content += f"*... et {rest} autre{'s' if rest > 1 else ''} jeu{'x' if rest > 1 else ''}*"
|
|
||||||
|
|
||||||
embed = discord.Embed(
|
|
||||||
title=f"🎮 Résultats ProtonDB - **{total_games} jeu{'x' if total_games > 1 else ''} trouvé{'s' if total_games > 1 else ''}**",
|
|
||||||
description=content,
|
|
||||||
color=0x5865F2
|
|
||||||
)
|
|
||||||
|
|
||||||
try :
|
|
||||||
await message.channel.send(embed=embed)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Échec de l'envoi de l'embed ProtonDB : {e}")
|
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_voice_state_update(member: Member, before, after):
|
async def on_voice_state_update(member: Member, before, after):
|
||||||
await on_voice_state_update_auto_rooms(bot, member, before, after)
|
await on_voice_state_update_auto_rooms(bot, member, before, after)
|
||||||
|
|||||||
@@ -910,9 +910,9 @@ async def handle_staff_help_command(message: Message, bot):
|
|||||||
if ConfigurationHelper().getValue('proton_db_enable_enable'):
|
if ConfigurationHelper().getValue('proton_db_enable_enable'):
|
||||||
public_commands.append(
|
public_commands.append(
|
||||||
"**🎮 ProtonDB**\n"
|
"**🎮 ProtonDB**\n"
|
||||||
"• `!protondb nom du jeu` ou `!pdb nom du jeu`\n"
|
"• `/protondb` — recherche par nom de jeu\n"
|
||||||
"Recherche un jeu sur ProtonDB pour vérifier sa compatibilité Linux\n"
|
"• Clic droit sur un message → Applications → **Rechercher sur ProtonDB**\n"
|
||||||
"Ex: `!pdb Elden Ring`"
|
"Ex. `/protondb` avec le paramètre *jeu* : Elden Ring"
|
||||||
)
|
)
|
||||||
|
|
||||||
from database.models import Commande
|
from database.models import Commande
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import logging
|
||||||
|
from typing import Any, List
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import app_commands
|
||||||
|
|
||||||
|
from database.helpers import ConfigurationHelper
|
||||||
|
from protondb import searhProtonDb
|
||||||
|
|
||||||
|
|
||||||
|
def _build_protondb_embed(games: List[Any]) -> discord.Embed:
|
||||||
|
total_games = len(games)
|
||||||
|
tier_colors = {'platinum': '🟣', 'gold': '🟡', 'silver': '⚪', 'bronze': '🟤', 'borked': '🔴'}
|
||||||
|
content = ""
|
||||||
|
max_games = 15
|
||||||
|
|
||||||
|
for count, game in enumerate(games[:max_games]):
|
||||||
|
g_name = str(game.get('name'))
|
||||||
|
g_id = str(game.get('id'))
|
||||||
|
tier = str(game.get('tier') or 'N/A').lower()
|
||||||
|
tier_icon = tier_colors.get(tier, '⚫')
|
||||||
|
|
||||||
|
new_entry = f"**[{g_name}](<https://www.protondb.com/app/{g_id}>)**\n{tier_icon} Classé **{tier.capitalize()}**"
|
||||||
|
|
||||||
|
ac_status = game.get('anticheat_status')
|
||||||
|
if ac_status:
|
||||||
|
status_lower = str(ac_status).lower()
|
||||||
|
ac_map = {
|
||||||
|
'supported': ('✅', 'Supporté'),
|
||||||
|
'running': ('⚠️', 'Fonctionne'),
|
||||||
|
'broken': ('❌', 'Cassé'),
|
||||||
|
'denied': ('🚫', 'Refusé'),
|
||||||
|
'planned': ('📅', 'Planifié')
|
||||||
|
}
|
||||||
|
ac_emoji, ac_label = ac_map.get(status_lower, ('❔', str(ac_status)))
|
||||||
|
acs = game.get('anticheats') or []
|
||||||
|
ac_list = ', '.join([str(ac) for ac in acs if ac])
|
||||||
|
new_entry += f" • [Anti-cheat {ac_emoji} {ac_label}"
|
||||||
|
if ac_list:
|
||||||
|
new_entry += f" ({ac_list})"
|
||||||
|
new_entry += f"](<https://areweanticheatyet.com/game/{g_id}>)"
|
||||||
|
|
||||||
|
new_entry += "\n\n"
|
||||||
|
|
||||||
|
if len(content) + len(new_entry) > 3900:
|
||||||
|
rest = len(games) - count
|
||||||
|
content += f"*... et {rest} autre{'s' if rest > 1 else ''} jeu{'x' if rest > 1 else ''}*"
|
||||||
|
break
|
||||||
|
|
||||||
|
content += new_entry
|
||||||
|
else:
|
||||||
|
rest = max(0, len(games) - max_games)
|
||||||
|
if rest > 0:
|
||||||
|
content += f"*... et {rest} autre{'s' if rest > 1 else ''} jeu{'x' if rest > 1 else ''}*"
|
||||||
|
|
||||||
|
return discord.Embed(
|
||||||
|
title=f"🎮 Résultats ProtonDB - **{total_games} jeu{'x' if total_games > 1 else ''} trouvé{'s' if total_games > 1 else ''}**",
|
||||||
|
description=content,
|
||||||
|
color=0x5865F2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _protondb_search_followup(interaction: discord.Interaction, query: str) -> None:
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
embed = _build_protondb_embed(games)
|
||||||
|
try:
|
||||||
|
await interaction.followup.send(embed=embed)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"ProtonDB : envoi embed : {e}")
|
||||||
|
|
||||||
|
|
||||||
|
@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):
|
||||||
|
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 = jeu.strip()
|
||||||
|
if not query:
|
||||||
|
await interaction.response.send_message(
|
||||||
|
"⚠️ Indique le nom d'un jeu.\nExemple : `/protondb jeu:Elden Ring`",
|
||||||
|
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)
|
||||||
Reference in New Issue
Block a user