import os import discord from discord.ext import commands from discord import app_commands import random as random_module import typing # Need this for Optional # Cache to store uploaded file URLs (local to this cog) file_url_cache = {} class RandomCog(commands.Cog): def __init__(self, bot): self.bot = bot # Updated _random_logic async def _random_logic( self, interaction_or_ctx, hidden: bool = False ) -> typing.Optional[str]: """Core logic for the random command. Returns an error message string or None if successful.""" # NSFW Check is_nsfw_channel = False channel = interaction_or_ctx.channel if isinstance(channel, discord.TextChannel) and channel.is_nsfw(): is_nsfw_channel = True elif isinstance( channel, discord.DMChannel ): # DMs are considered NSFW for this purpose is_nsfw_channel = True if not is_nsfw_channel: # Return error message directly, ephemeral handled by caller return "This command can only be used in age-restricted (NSFW) channels or DMs." directory = os.getenv("UPLOAD_DIRECTORY") if not directory: return "UPLOAD_DIRECTORY is not set in the .env file." if not os.path.isdir(directory): return ( "The specified UPLOAD_DIRECTORY does not exist or is not a directory." ) files = [ f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f)) ] if not files: return "The specified directory is empty." # Attempt to send a random file, handling potential size issues original_files = list(files) # Copy for checking if all files failed while files: chosen_file_name = random_module.choice(files) file_path = os.path.join(directory, chosen_file_name) # Check cache first if chosen_file_name in file_url_cache: # For interactions, defer if not already done, using the hidden flag if ( not isinstance(interaction_or_ctx, commands.Context) and not interaction_or_ctx.response.is_done() ): await interaction_or_ctx.response.defer( ephemeral=hidden ) # Defer before sending cached URL # Send cached URL if isinstance(interaction_or_ctx, commands.Context): await interaction_or_ctx.reply( file_url_cache[chosen_file_name] ) # Prefix commands can't be ephemeral else: await interaction_or_ctx.followup.send( file_url_cache[chosen_file_name], ephemeral=hidden ) return None # Indicate success try: # Determine how to send the file based on context/interaction if isinstance(interaction_or_ctx, commands.Context): message = await interaction_or_ctx.reply( file=discord.File(file_path) ) # Use reply for context else: # It's an interaction # Interactions need followup for files after defer() if not interaction_or_ctx.response.is_done(): await interaction_or_ctx.response.defer( ephemeral=hidden ) # Defer before sending file # Send file ephemerally if hidden is True message = await interaction_or_ctx.followup.send( file=discord.File(file_path), ephemeral=hidden ) # Cache the URL if successfully sent if message and message.attachments: file_url_cache[chosen_file_name] = message.attachments[0].url # Success, no further message needed return None else: # Should not happen if send succeeded, but handle defensively files.remove(chosen_file_name) print( f"Warning: File {chosen_file_name} sent but no attachment URL found." ) # Log warning continue except discord.HTTPException as e: if e.code == 40005: # Request entity too large print(f"File too large: {chosen_file_name}") files.remove(chosen_file_name) continue # Try another file else: print(f"HTTP Error sending file: {e}") # Return error message directly, ephemeral handled by caller return f"Failed to upload the file due to an HTTP error: {e}" except Exception as e: print(f"Generic Error sending file: {e}") # Return error message directly, ephemeral handled by caller return f"An unexpected error occurred while uploading the file: {e}" # If loop finishes without returning/sending, all files were too large # Return error message directly, ephemeral handled by caller return "All files in the directory were too large to upload." # --- Prefix Command --- @commands.command(name="random") async def random(self, ctx: commands.Context): """Upload a random NSFW image from the configured directory.""" # Call _random_logic, hidden is False by default and irrelevant for prefix response = await self._random_logic(ctx) if response is not None: await ctx.reply(response) # --- Slash Command --- # Updated signature and logic @app_commands.command( name="random", description="Upload a random NSFW image from the configured directory", ) @app_commands.describe( hidden="Set to True to make the response visible only to you (default: False)" ) async def random_slash( self, interaction: discord.Interaction, hidden: bool = False ): """Slash command version of random.""" # Pass hidden parameter to logic response = await self._random_logic(interaction, hidden=hidden) # If response is None, the logic already sent the file via followup/deferral if response is not None: # An error occurred # Ensure interaction hasn't already been responded to or deferred if not interaction.response.is_done(): # Send error message ephemerally if hidden is True OR if it's the NSFW channel error ephemeral_error = hidden or response.startswith( "This command can only be used" ) await interaction.response.send_message( response, ephemeral=ephemeral_error ) else: # If deferred, use followup. Send ephemerally based on hidden flag. await interaction.followup.send(response, ephemeral=hidden) async def setup(bot): await bot.add_cog(RandomCog(bot))