From f805adf5b895783b44f9fbed338cd74ebafca185 Mon Sep 17 00:00:00 2001 From: Timiliris_420 Date: Thu, 27 Feb 2025 16:51:54 +0100 Subject: [PATCH] Refactor commands to use slash commands and embeds This commit refactors the bot to use slash commands instead of prefix commands for addon and schematic searches. It also updates the help command to use an embed and adds a new command for displaying the website link. The search functions now use interaction.followup.send() to send messages after deferring the response. --- addonsearch.py | 20 ++++----- bot.py | 103 ++++++++++++++++++++++++--------------------- requirements.txt | 4 ++ schematicsearch.py | 32 +++++++------- 4 files changed, 85 insertions(+), 74 deletions(-) create mode 100644 requirements.txt diff --git a/addonsearch.py b/addonsearch.py index eb03eb0..0aca1b9 100644 --- a/addonsearch.py +++ b/addonsearch.py @@ -1,31 +1,31 @@ import discord import meilisearch -import json import dotenv import os + dotenv.load_dotenv() client = meilisearch.Client(os.getenv("SEARCH-URL"), os.getenv("SEARCH-TOKEN")) loaders = ["Forge", "NeoForge", "Fabric", "Quilt"] index = client.index("addons") -async def addonsearch(ctx, query, limit=1): +async def addonsearch(interaction: discord.Interaction, query: str, limit: int = 1): request = index.search(query, {"limit": limit}) - data = request.get("hits", []) # Safely get 'hits' (default to empty list if not found) - await send_embeds(ctx, data) + data = request.get("hits", []) # Récupérer en toute sécurité 'hits' ou une liste vide + await send_embeds(interaction, data) return data -async def send_embeds(message, data): - for mod_data in data: # `data` is a list of addon dictionaries +async def send_embeds(interaction: discord.Interaction, data: list[dict]): + for mod_data in data: # `data` est une liste de dictionnaires d'addons embed = await gen_embed(mod_data) - await message.reply(embed=embed) + await interaction.followup.send(embed=embed) -def format_loaders(loaders): +def format_loaders(loaders: list[str]) -> str: if not loaders: return "No loaders available" return " ".join([f"{loader}" for loader in loaders]) -async def gen_embed(data): - loaders_field = format_loaders(data.get("loaders", [])) # Use `.get()` for safety +async def gen_embed(data: dict) -> discord.Embed: + loaders_field = format_loaders(data.get("loaders", [])) embed = discord.Embed(title=data["name"], description=data["description"], color=0x689AEE) embed.set_author(name=data["author"]) embed.set_thumbnail(url=data["icon"]) diff --git a/bot.py b/bot.py index 2e12217..d5d1272 100644 --- a/bot.py +++ b/bot.py @@ -1,24 +1,29 @@ import discord +from discord import app_commands from discord.ext import commands from colorama import Back, Style from addonsearch import addonsearch from schematicsearch import schematicsearch import dotenv import os + dotenv.load_dotenv() intents = discord.Intents.default() -intents.message_content = True # Required for reading user messages +intents.message_content = True bot = commands.Bot(command_prefix="??", intents=intents) +# Remove the old help command bot.remove_command("help") @bot.event async def on_ready(): + # Synchronize slash commands with Discord + await bot.tree.sync() print(f"{Back.GREEN}Logged in as {bot.user}{Style.RESET_ALL}") -@bot.command() -async def help(ctx): +@bot.tree.command(name="help", description="Displays the list of available commands") +async def help_command(interaction: discord.Interaction): embed = discord.Embed( title="Commands available", description="Addons & Schematics commands", @@ -26,12 +31,12 @@ async def help(ctx): ) embed.add_field( name="Addon Commands", - value="??addon search [how many default=1] - Search for addons on Blueprint.", + value="/addon search [limit=1] - Search for addons on Blueprint.", inline=False ) embed.add_field( name="Schematic Commands", - value="??schematic search [how many default=1]", + value="/schematic search [limit=1]", inline=False ) embed.add_field( @@ -39,57 +44,61 @@ async def help(ctx): value=""" <> - needed [] - optional -""", + """, inline=False ) - await ctx.reply(embed=embed) - -# Create "addon" group command -@bot.group(invoke_without_command=True) -async def addon(ctx): - await ctx.send("Use `??addon search ` to find addons.") - -@bot.group(invoke_without_command=True) -async def schematic(ctx): - await ctx.send("Use `??schematic search ` to find schematics.") + await interaction.response.send_message(embed=embed) -# Subcommand for "addon" -@addon.command() -async def search(ctx, *, query: str): - # Extract the number of results from the end of the query (if available) - parts = query.split() - if parts[-1].isdigit(): - limit = int(parts[-1]) - query = " ".join(parts[:-1]) # Remove the number from the query - else: - limit = 1 # Default to 1 result if no number is specified - - # Ensure the limit is between 1 and 5 +# Slash command for addon search +@bot.tree.command(name="addon", description="Manages commands related to addons") +async def addon_command(interaction: discord.Interaction, query: str, limit: int = 1): limit = max(1, min(limit, 5)) - msg = await ctx.send(f"Searching for {limit} addon(s) related to: `{query}`") - await msg.delete(delay=3) + # Defer the response to avoid InteractionResponded error + await interaction.response.defer(ephemeral=True) + + # Search for addons + await addonsearch(interaction=interaction, query=query, limit=limit) - await addonsearch(ctx=ctx, query=query, limit=limit) +# Slash command for schematic search +@bot.tree.command(name="schematic", description="Manages commands related to schematics") +async def schematic_command(interaction: discord.Interaction, query: str, limit: int = 1): + limit = max(1, min(limit, 5)) + + # Defer the response to avoid InteractionResponded error + await interaction.response.defer(ephemeral=True) + + # Search for schematics + await schematicsearch(interaction=interaction, query=query, limit=limit) -# Subcommand for "schematic" -@schematic.command() -async def search(ctx, *, query: str): - # Extract the number of results from the end of the query (if available) - parts = query.split() - if parts[-1].isdigit(): - limit = int(parts[-1]) - query = " ".join(parts[:-1]) # Remove the number from the query - else: - limit = 1 # Default to 1 result if no number is specified +@bot.tree.command(name="link", description="Displays the link to the official website") +async def site_command(interaction: discord.Interaction): + embed = discord.Embed( + title="Official Website", + description="Visit our website to discover more addons and schematics!", + color=discord.Color.blue(), + url="https://your-website.com" # Replace with your actual URL + ) - # Ensure the limit is between 1 and 5 - limit = max(1, min(limit, 5)) + # Add an image to the embed (optional) + embed.set_thumbnail(url="https://your-website.com/logo.png") # Replace with your logo URL + + # Add additional information + embed.add_field( + name="Latest Updates", + value="Check out the latest addons and schematics added to our site!", + inline=False + ) + + embed.add_field( + name="Support", + value="Need help? Visit our forum or contact us directly on the site.", + inline=False + ) - msg = await ctx.send(f"Searching for {limit} schematic(s) related to: `{query}`") - await msg.delete(delay=3) + # Add a footer + embed.set_footer(text="© 2025 Your Name - All rights reserved") - # Fetch data - await schematicsearch(ctx=ctx, query=query, limit=limit) + await interaction.response.send_message(embed=embed) bot.run(os.getenv("TOKEN")) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5eaaab7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +discord.py +colorama +python-dotenv +meilisearch \ No newline at end of file diff --git a/schematicsearch.py b/schematicsearch.py index 5966a95..61b1588 100644 --- a/schematicsearch.py +++ b/schematicsearch.py @@ -1,22 +1,23 @@ -import discord import meilisearch -import json -import dotenv +import discord import os -dotenv.load_dotenv() + +# Charger la configuration de Meilisearch depuis les variables d'environnement client = meilisearch.Client(os.getenv("SEARCH-URL"), os.getenv("SEARCH-TOKEN")) -loaders = ["forge", "neoforge", "fabric", "quilt"] index = client.index("schematics") -async def schematicsearch(ctx, query, limit=1): +async def schematicsearch(interaction: discord.Interaction, query: str, limit: int = 1): + # Effectuer la recherche dans Meilisearch request = index.search(query, {"limit": limit}) - data = request.get("hits", []) # Safely get 'hits' (default to empty list if not found) - await send_embeds(ctx.message, data) + data = request.get("hits", []) + + # Envoyer la réponse sous forme d'embed + await send_embeds(interaction, data) -async def send_embeds(message, data): - for mod_data in data: # `data` is a list of addon dictionaries +async def send_embeds(interaction: discord.Interaction, data): + for mod_data in data: embed = await gen_embed(mod_data) - await message.reply(embed=embed) + await interaction.followup.send(embed=embed) # Utiliser followup.send() après defer() def format_loaders(loaders): if not loaders: @@ -24,14 +25,11 @@ def format_loaders(loaders): return " ".join([f"{loader}" for loader in loaders]) async def gen_embed(data): - loaders_field = format_loaders(data.get("modloaders", [])) # Use `.get()` for safety + loaders_field = format_loaders(data.get("modloaders", [])) embed = discord.Embed(title=data["title"], description=data["description"], color=0x689AEE) - embed.set_author(name=data["authors"][0]) - embed.set_thumbnail(url=data["image_url"]) + embed.set_author(name=data["$id"]) + embed.set_thumbnail(url=data.get("image_urls", [None])[0]) embed.add_field(name="Loaders", value=loaders_field, inline=False) embed.add_field(name="Download", value=f"https://nottelling.youthe.schematic/schematics/{data['slug']}", inline=False) - embed.add_field(name="Author", value=f"Author: {data['authors'][0]}") embed.set_footer(text=f"Downloads: {data['downloads']}") return embed - -