From 1842cd85da204dbd36e60aa3a71fbbb89cdc7a23 Mon Sep 17 00:00:00 2001 From: Lilac-Rose Date: Tue, 30 Dec 2025 00:34:21 +0100 Subject: [PATCH] python programming at 6pm gone wrong cops called --- commands/help.py | 482 +++++++++++++++++++++--------------------- suggestion/suggest.py | 2 +- 2 files changed, 245 insertions(+), 239 deletions(-) diff --git a/commands/help.py b/commands/help.py index bfc93a1..7be6b41 100644 --- a/commands/help.py +++ b/commands/help.py @@ -1,249 +1,255 @@ +""" +Custom help command that works as both !help and /help +Supports searching for specific commands +Place in commands/ folder +""" + import discord -from discord import app_commands from discord.ext import commands -from typing import Dict, List - - -class HelpView(discord.ui.View): - - def __init__(self, bot, cog_commands: Dict[str, List[str]]): - super().__init__(timeout=180) - self.bot = bot - self.cog_commands = cog_commands - self.current_page = "home" - - self.cog_info = { - "commands": {"name": "General", "emoji": "⚙️"}, - "moderation": {"name": "Moderation", "emoji": "🛡️"}, - "xp": {"name": "XP & Leveling", "emoji": "⭐"}, - "sparkle": {"name": "Sparkle", "emoji": "✨"}, - "image": {"name": "Image", "emoji": "🖼️"}, - "suggestion": {"name": "Suggestions", "emoji": "💡"}, - "birthday": {"name": "Birthdays", "emoji": "🎂"}, - "embed": {"name": "Embed", "emoji": "📝"}, - "embeds": {"name": "Embeds", "emoji": "📜"}, - "profiles": {"name": "Profiles", "emoji": "👤"}, - "wordle": {"name": "Wordle", "emoji": "🟩"}, - } - - self.add_cog_buttons() - - home_button = discord.ui.Button( - label="Home", - emoji="🏠", - style=discord.ButtonStyle.primary, - custom_id="help_home", - row=4 - ) - home_button.callback = self.show_home - self.add_item(home_button) - - def add_cog_buttons(self): - row = 0 - col = 0 - for cog_key in [ - "commands", "moderation", "xp", "sparkle", - "image", "suggestion", "birthday", - "embed", "embeds", "profiles", "wordle" - ]: - if cog_key in self.cog_commands and self.cog_commands[cog_key]: - info = self.cog_info.get(cog_key, {"emoji": "📦", "name": cog_key.title()}) - - button = discord.ui.Button( - label=info["name"], - emoji=info["emoji"], - style=discord.ButtonStyle.secondary, - custom_id=f"help_{cog_key}", - row=row - ) - - def make_callback(cog_name=cog_key): - async def callback(interaction: discord.Interaction): - try: - await self.show_cog(interaction, cog_name) - except Exception as e: - print(f"Button callback error for {cog_name}: {e}") - import traceback - traceback.print_exc() - try: - await interaction.response.send_message(f"Error: {e}", ephemeral=True) - except: - pass - return callback - - button.callback = make_callback() - self.add_item(button) - - col += 1 - if col >= 5: - col = 0 - row += 1 - - async def show_home(self, interaction: discord.Interaction): - try: - self.current_page = "home" - embed = self.create_home_embed(interaction) - await interaction.response.edit_message(embed=embed, view=self) - except discord.errors.InteractionResponded: - await interaction.followup.edit_message(interaction.message.id, embed=embed, view=self) - - async def show_cog(self, interaction: discord.Interaction, cog_name: str): - try: - self.current_page = cog_name - embed = self.create_cog_embed(interaction, cog_name) - await interaction.response.edit_message(embed=embed, view=self) - except discord.errors.InteractionResponded: - await interaction.followup.edit_message(interaction.message.id, embed=embed, view=self) - - def create_home_embed(self, interaction: discord.Interaction) -> discord.Embed: - color = discord.Color.purple() - try: - embed_color_cog = self.bot.get_cog("EmbedColor") - if embed_color_cog: - color = embed_color_cog.get_user_color(interaction.user) - except: - pass - - embed = discord.Embed( - title="🤖 Lacie Bot - Help", - description=( - "Welcome to Lacie! Use the buttons below to explore the different command categories.\n\n" - "**💡 Found a bug or have an idea?**\n" - "Use `/suggest` to report bugs or suggest new features! Your feedback helps improve the bot." - ), - color=color - ) - - embed.add_field( - name="🔗 Links", - value=( - "• [GitHub Repository](https://github.com/Lilac-Rose/Lacie)\n" - "• [Web Dashboard](https://bots.lilacrose.dev/lacie/)" - ), - inline=False - ) - - embed.set_footer(text="Created with 💜 by Lilac Aria Rose") - return embed - - def create_cog_embed(self, interaction: discord.Interaction, cog_name: str) -> discord.Embed: - info = self.cog_info.get(cog_name, {"emoji": "📦", "name": cog_name.title()}) - - color = discord.Color.blue() - try: - embed_color_cog = self.bot.get_cog("EmbedColor") - if embed_color_cog: - color = embed_color_cog.get_user_color(interaction.user) - except: - pass - - embed = discord.Embed( - title=f"{info['emoji']} {info['name']} Commands", - description="Here are all the commands in this category:", - color=color - ) - - if cog_name in self.cog_commands: - commands_list = "\n".join(sorted(set(self.cog_commands[cog_name]))) - if len(commands_list) > 4096: - chunks = [] - current_chunk = [] - current_length = 0 - for cmd in sorted(set(self.cog_commands[cog_name])): - if current_length + len(cmd) + 1 > 1024: - chunks.append("\n".join(current_chunk)) - current_chunk = [cmd] - current_length = len(cmd) - else: - current_chunk.append(cmd) - current_length += len(cmd) + 1 - if current_chunk: - chunks.append("\n".join(current_chunk)) - for i, chunk in enumerate(chunks): - field_name = "Commands" if i == 0 else f"Commands (cont. {i+1})" - embed.add_field(name=field_name, value=chunk, inline=False) - else: - embed.description = commands_list - else: - embed.description = "No commands found in this category." - - embed.set_footer( - text=f"Use the buttons to navigate • Total: {len(set(self.cog_commands.get(cog_name, [])))} commands" - ) - return embed - - async def on_timeout(self): - for item in self.children: - item.disabled = True - +from discord import app_commands +from typing import Optional +import difflib class Help(commands.Cog): def __init__(self, bot): self.bot = bot - - @app_commands.command(name="help", description="Shows all available commands organized by category") - async def help_command(self, interaction: discord.Interaction): - try: - await interaction.response.defer(thinking=True) - cog_commands = {} - - # Process slash commands safely - for command in self.bot.tree.get_commands(): - cog = getattr(command, "binding", None) # <-- FIX HERE - if cog: - module = cog.__class__.__module__ - folder = module.split(".")[0] if "." in module else "other" + + def get_command_signature(self, command): + """Get the usage signature for a command""" + if isinstance(command, commands.HybridCommand): + # For hybrid commands, show both formats + params = [] + for name, param in command.clean_params.items(): + if param.default == param.empty: + params.append(f"<{name}>") else: - folder = "other" - - if folder == "events": - continue - - cog_commands.setdefault(folder, []) - cmd_desc = command.description or "No description" - cog_commands[folder].append(f"`/{command.name}` - {cmd_desc}") - - # Process prefix commands - for cmd_name, command in self.bot.all_commands.items(): - if command.name != cmd_name: - continue - - if command.cog: - module = command.cog.__class__.__module__ - folder = module.split(".")[0] if "." in module else "other" + params.append(f"[{name}]") + param_str = " ".join(params) + return f"!{command.name} {param_str}\n/{command.name} {param_str}" + else: + return f"{command.qualified_name} {command.signature}" + + def get_all_commands(self): + """Get all commands organized by cog""" + cog_commands = {} + + for cog_name, cog in self.bot.cogs.items(): + commands_list = [] + + # Get regular commands + for cmd in cog.get_commands(): + if not cmd.hidden: + commands_list.append(cmd) + + # Get app commands (slash only) + if hasattr(cog, '__cog_app_commands__'): + for cmd in cog.__cog_app_commands__: + if isinstance(cmd, app_commands.Command): + commands_list.append(cmd) + elif isinstance(cmd, app_commands.Group): + # Add group commands + commands_list.append(cmd) + + if commands_list: + cog_commands[cog_name] = commands_list + + # Get commands not in cogs + no_cog_commands = [cmd for cmd in self.bot.commands if not cmd.cog and not cmd.hidden] + if no_cog_commands: + cog_commands["Other"] = no_cog_commands + + return cog_commands + + def search_commands(self, query): + """Search for commands matching the query""" + query = query.lower() + results = [] + + for cog_name, cog in self.bot.cogs.items(): + for cmd in cog.get_commands(): + if not cmd.hidden: + # Check command name + if query in cmd.name.lower(): + results.append((cmd, cog_name, 100)) + # Check command aliases + elif any(query in alias.lower() for alias in cmd.aliases): + results.append((cmd, cog_name, 90)) + # Check description + elif cmd.description and query in cmd.description.lower(): + results.append((cmd, cog_name, 70)) + # Check help text + elif cmd.help and query in cmd.help.lower(): + results.append((cmd, cog_name, 60)) + + # Sort by relevance + results.sort(key=lambda x: x[2], reverse=True) + return results + + def create_help_embed(self, title, description=None): + """Create a base help embed""" + embed = discord.Embed( + title=title, + description=description, + color=discord.Color.blurple() + ) + embed.set_footer(text="Use !help or /help for more info on a specific command") + return embed + + @commands.hybrid_command(name="help", description="Shows help information for commands") + @app_commands.describe(command="The command to get help for") + async def help_command(self, ctx: commands.Context, *, command: Optional[str] = None): + """ + Get help for bot commands + + Usage: + !help - Show all commands + !help - Get detailed help for a specific command + !help - Search for commands + """ + + if command: + # Try to find exact command match first + cmd = self.bot.get_command(command.lower()) + + if cmd: + # Show detailed help for specific command + embed = self.create_help_embed( + title=f"Help: {cmd.name}", + description=cmd.help or cmd.description or "No description available" + ) + + # Usage + embed.add_field( + name="Usage", + value=f"```\n{self.get_command_signature(cmd)}\n```", + inline=False + ) + + # Aliases + if cmd.aliases: + embed.add_field( + name="Aliases", + value=", ".join(f"`{alias}`" for alias in cmd.aliases), + inline=False + ) + + # Category + if cmd.cog: + embed.add_field(name="Category", value=cmd.cog.qualified_name, inline=True) + + await ctx.send(embed=embed) + else: + # Search for commands + results = self.search_commands(command) + + if not results: + # Suggest similar commands + all_cmd_names = [c.name for c in self.bot.commands] + suggestions = difflib.get_close_matches(command.lower(), all_cmd_names, n=3, cutoff=0.6) + + embed = discord.Embed( + title="❌ Command Not Found", + description=f"No command found matching `{command}`", + color=discord.Color.red() + ) + + if suggestions: + embed.add_field( + name="Did you mean?", + value="\n".join(f"• `{s}`" for s in suggestions), + inline=False + ) + + await ctx.send(embed=embed) else: - folder = "other" - - if folder == "events": - continue - - cog_commands.setdefault(folder, []) - cmd_desc = command.help or command.brief or "No description" - alias_text = f" (aliases: {', '.join(f'!{a}' for a in command.aliases)})" if command.aliases else "" - cog_commands[folder].append(f"`!{command.name}`{alias_text} - {cmd_desc}") - - view = HelpView(self.bot, cog_commands) - embed = view.create_home_embed(interaction) - - print(f"DEBUG: Found cogs: {list(cog_commands.keys())}") - for cog_name, cmds in cog_commands.items(): - print(f" {cog_name}: {len(set(cmds))} unique commands") - - await interaction.followup.send(embed=embed, view=view) - - except Exception as e: - import traceback - error_msg = "".join(traceback.format_exception(type(e), e, e.__traceback__)) - error_embed = discord.Embed( - title="❌ Error in /help command", - description=f"```py\n{error_msg[:4000]}```", - color=discord.Color.red() + # Show search results + embed = self.create_help_embed( + title=f"🔍 Search Results for '{command}'", + description=f"Found {len(results)} matching command(s)" + ) + + for cmd, cog_name, score in results[:10]: # Limit to 10 results + desc = cmd.description or cmd.help or "No description" + if len(desc) > 100: + desc = desc[:97] + "..." + + embed.add_field( + name=f"{cmd.name} ({cog_name})", + value=desc, + inline=False + ) + + if len(results) > 10: + embed.set_footer(text=f"Showing top 10 of {len(results)} results. Use !help for details.") + + await ctx.send(embed=embed) + else: + # Show all commands organized by category + cog_commands = self.get_all_commands() + + embed = self.create_help_embed( + title="📚 Bot Commands", + description=f"Prefix: `!` | Total Commands: {len(list(self.bot.commands))}\n\n" + "Commands work with both `!` and `/`" ) - try: - await interaction.followup.send(embed=error_embed) - except: - await interaction.response.send_message(embed=error_embed) - + + for cog_name, cmds in sorted(cog_commands.items()): + if not cmds: + continue + + # Group commands by name + cmd_names = [] + seen = set() + + for cmd in cmds: + if isinstance(cmd, (commands.Command, commands.HybridCommand)): + name = cmd.name + elif isinstance(cmd, (app_commands.Command, app_commands.Group)): + name = cmd.name + else: + continue + + if name not in seen: + cmd_names.append(f"`{name}`") + seen.add(name) + + if cmd_names: + embed.add_field( + name=f"**{cog_name}**", + value=" • ".join(cmd_names), + inline=False + ) + + await ctx.send(embed=embed) + + @commands.Cog.listener() + async def on_command_error(self, ctx: commands.Context, error): + """Show help if command not found""" + if isinstance(error, commands.CommandNotFound): + # Extract the attempted command + cmd_used = ctx.message.content.split()[0][len(ctx.prefix):] + + # Suggest similar commands + all_cmd_names = [c.name for c in self.bot.commands] + suggestions = difflib.get_close_matches(cmd_used, all_cmd_names, n=3, cutoff=0.6) + + embed = discord.Embed( + title="❓ Unknown Command", + description=f"Command `{cmd_used}` not found.", + color=discord.Color.orange() + ) + + if suggestions: + embed.add_field( + name="Did you mean?", + value="\n".join(f"• `!{s}` or `/{s}`" for s in suggestions), + inline=False + ) + + embed.set_footer(text="Use !help to see all commands") + await ctx.send(embed=embed, delete_after=10) async def setup(bot): - await bot.add_cog(Help(bot)) + # Remove default help command + bot.remove_command('help') + await bot.add_cog(Help(bot)) \ No newline at end of file diff --git a/suggestion/suggest.py b/suggestion/suggest.py index 1928011..0188e64 100644 --- a/suggestion/suggest.py +++ b/suggestion/suggest.py @@ -269,7 +269,7 @@ class Suggestion(commands.Cog): print(f"Error in suggest command: {e}") await interaction.followup.send(f"❌ An error occurred: {e}") - suggestion = app_commands.Group(name="suggestion", description="Manage suggestions") + suggestion = app_commands.hybrid_group(name="suggestion", description="Manage suggestions") @suggestion.command(name="view", description="View full details of a suggestion") async def suggestion_view(self, interaction: discord.Interaction, suggestion_id: int):