From c05d179f20a17b881c293ac2677e40b3c67c26ab Mon Sep 17 00:00:00 2001 From: SimolZimol <70102430+SimolZimol@users.noreply.github.com> Date: Wed, 20 Aug 2025 23:44:21 +0200 Subject: [PATCH] modified: bot.py --- bot.py | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 254 insertions(+), 31 deletions(-) diff --git a/bot.py b/bot.py index 2c796a8..d1f4614 100644 --- a/bot.py +++ b/bot.py @@ -3655,64 +3655,158 @@ async def viewwarn(ctx, warning_id: int): await send_response(embed=embed) @client.hybrid_command() -async def mute(ctx, user: discord.User, duration: str, *, reason: str = "No reason provided"): +async def mute(ctx, user: discord.User, duration: str, reason: str = "No reason provided", message_id: str = None): """Mutes a user for a specified duration (Requires Permission Level 5 or higher) + Usage: + /mute @user 10m "Inappropriate behavior" + /mute @user 1h "Bad language" 1407754702564884622 + + Parameters: + - user: The user to mute + - duration: Duration (10m, 1h, 2d) + - reason: Reason for the mute + - message_id: Optional message ID to reference + Duration examples: - 10m = 10 minutes - 1h = 1 hour - 2d = 2 days """ + # Check if it's a slash command and defer if needed + is_slash_command = hasattr(ctx, 'interaction') and ctx.interaction + if is_slash_command: + await ctx.defer() + + # Helper function for sending responses + async def send_response(content=None, embed=None, ephemeral=False, file=None): + try: + if is_slash_command: + await ctx.followup.send(content=content, embed=embed, ephemeral=ephemeral, file=file) + else: + await ctx.send(content=content, embed=embed, file=file) + except Exception as e: + logger.error(f"Error sending response: {e}") + # Fallback to regular send if followup fails + try: + await ctx.send(content=content, embed=embed, file=file) + except: + pass + try: + # Parse message ID from reason if it looks like a message ID + original_reason = reason + message_data = None + + # Check if reason ends with a potential message ID + reason_words = reason.split() + if len(reason_words) > 1 and len(reason_words[-1]) >= 17 and len(reason_words[-1]) <= 20 and reason_words[-1].isdigit(): + # Extract message ID from reason + message_id = reason_words[-1] + reason = " ".join(reason_words[:-1]) # Remove message ID from reason + + # Try to get message data if message ID was provided + if message_id: + try: + message_id_int = int(message_id) + except ValueError: + await send_response(content=f"❌ Invalid message ID: {message_id}") + return + + # Try to get message data from current channel first + message_data = await get_message_data(ctx.channel, message_id_int) + + # If not found in current channel, try other channels + if message_data is None: + # Limit search to avoid spam - only check first 10 channels plus current channel + channels_to_check = [ctx.channel] + [ch for ch in ctx.guild.text_channels[:10] if ch.id != ctx.channel.id] + for channel in channels_to_check[1:]: # Skip current channel, already checked + try: + message_data = await get_message_data(channel, message_id_int) + if message_data is not None: + break + except discord.Forbidden: + continue + # Load moderator data mod_data = await load_user_data(ctx.author.id, ctx.guild.id) # Check moderation rights if not check_moderation_permission(mod_data["permission"]): - await ctx.send("❌ You don't have permission to use this command. (Requires Permission Level 5 or higher)") + embed = discord.Embed( + title="❌ Insufficient Permissions", + description="You need moderation permissions (Level 5 or higher) to use this command.", + color=0xff0000 + ) + await send_response(embed=embed, ephemeral=True) + return + + # Cannot mute yourself + if user.id == ctx.author.id: + embed = discord.Embed( + title="❌ Invalid Action", + description="You cannot mute yourself!", + color=0xff0000 + ) + await send_response(embed=embed, ephemeral=True) return # Parse duration time_units = {'m': 60, 'h': 3600, 'd': 86400} - if not duration[-1] in time_units or not duration[:-1].isdigit(): - await ctx.send("❌ Invalid time format. Use: 10m, 1h, 2d") + if not duration or not duration[-1] in time_units or not duration[:-1].isdigit(): + embed = discord.Embed( + title="❌ Invalid Duration", + description="Invalid time format. Use: 10m, 1h, 2d", + color=0xff0000 + ) + await send_response(embed=embed, ephemeral=True) return duration_seconds = int(duration[:-1]) * time_units[duration[-1]] end_time = datetime.now() + timedelta(seconds=duration_seconds) - # Hole Member-Objekt + # Get member object member = ctx.guild.get_member(user.id) if not member: - await ctx.send("❌ Benutzer nicht auf diesem Server gefunden.") + embed = discord.Embed( + title="❌ User Not Found", + description="User not found on this server.", + color=0xff0000 + ) + await send_response(embed=embed, ephemeral=True) return - # Lade Guild-Einstellungen + # Load guild settings guild_settings = get_guild_settings(ctx.guild.id) - # Speichere aktuelle Rollen + # Save current roles await save_user_roles(user.id, ctx.guild.id, member.roles) - # Entferne alle Rollen außer @everyone + # Remove all roles except @everyone roles_to_remove = [role for role in member.roles if not role.is_default()] if roles_to_remove: await member.remove_roles(*roles_to_remove, reason=f"Muted by {ctx.author}") - # Hole oder erstelle Mute-Rolle basierend auf Einstellungen + # Get or create mute role mute_role = await get_or_create_mute_role(ctx.guild, guild_settings) if not mute_role: - await ctx.send("❌ Konnte Mute-Rolle nicht finden oder erstellen. Überprüfe die Server-Einstellungen.") + embed = discord.Embed( + title="❌ Mute Role Error", + description="Could not find or create mute role. Check server settings.", + color=0xff0000 + ) + await send_response(embed=embed, ephemeral=True) return - # Vergebe Mute-Rolle + # Add mute role await member.add_roles(mute_role, reason=f"Muted by {ctx.author} for {duration}") - # Update User-Daten + # Update user data user_data = await load_user_data(user.id, ctx.guild.id) user_data["mutes"] += 1 update_user_data(user.id, ctx.guild.id, "mutes", user_data["mutes"]) - # Erstelle Active Process für Auto-Unmute + # Create active process for auto-unmute process_data = { "user_id": user.id, "guild_id": ctx.guild.id, @@ -3732,28 +3826,103 @@ async def mute(ctx, user: discord.User, duration: str, *, reason: str = "No reas data=process_data ) - # Erstelle Embed + # Create embed embed = discord.Embed( - title="🔇 Benutzer gemutet", - description=f"{user.mention} wurde gemutet.", + title="🔇 User Muted", + description=f"{user.mention} has been muted.", color=0xff0000, timestamp=datetime.now() ) - embed.add_field(name="Dauer", value=duration, inline=True) - embed.add_field(name="Endet am", value=end_time.strftime("%Y-%m-%d %H:%M:%S"), inline=True) - embed.add_field(name="Grund", value=reason, inline=False) - embed.add_field(name="Moderator", value=ctx.author.mention, inline=True) - embed.add_field(name="Mute-Count", value=f"{user_data['mutes']}", inline=True) - embed.set_footer(text=f"User ID: {user.id} | Process ID: {str(process_uuid)[:8]}") + embed.add_field(name="⏱️ Duration", value=duration, inline=True) + embed.add_field(name="⏰ Ends At", value=f"", inline=True) + embed.add_field(name="📝 Reason", value=reason, inline=False) + embed.add_field(name="👮 Moderator", value=ctx.author.mention, inline=True) + embed.add_field(name="🔇 Mute Count", value=f"{user_data['mutes']}", inline=True) - await ctx.send(embed=embed) + # Add message information if available + if message_data: + message_info = f"**Message ID:** `{message_data['id']}`\n" + message_info += f"**Channel:** <#{message_data['channel_id']}>\n" + message_info += f"**Author:** <@{message_data['author_id']}>\n" + if message_data['content']: + content_preview = message_data['content'][:200] + "..." if len(message_data['content']) > 200 else message_data['content'] + message_info += f"**Content:** {content_preview}" + embed.add_field(name="📄 Referenced Message", value=message_info, inline=False) + + # Process attachments for archival if any + if message_data.get('attachments'): + try: + attachments_data = json.loads(message_data['attachments']) + if attachments_data: + attachment_info = "" + for i, att in enumerate(attachments_data[:3]): # Show first 3 attachments + attachment_info += f"• {att.get('filename', 'Unknown file')}\n" + if len(attachments_data) > 3: + attachment_info += f"• +{len(attachments_data) - 3} more attachments" + embed.add_field(name="📎 Archived Attachments", value=attachment_info, inline=False) + except: + pass + elif message_id: + embed.add_field(name="📄 Referenced Message", value=f"Message ID: `{message_id}` (Message not found or inaccessible)", inline=False) + + embed.set_footer(text=f"User ID: {user.id} | Process ID: {str(process_uuid)[:8]}") + embed.set_thumbnail(url=user.display_avatar.url) + + await send_response(embed=embed) + + # Log the mute action + log_additional_info = { + "Duration": duration, + "Mute Count": str(user_data['mutes']), + "Process ID": str(process_uuid)[:8] + } + + if message_data: + log_additional_info["Referenced Message"] = f"ID: {message_data['id']} in <#{message_data['channel_id']}>" + + await log_moderation_action( + guild=ctx.guild, + action_type="mute", + moderator=ctx.author, + target_user=user, + reason=reason, + duration=duration, + additional_info=log_additional_info + ) + + # Try to DM the user + try: + dm_embed = discord.Embed( + title="🔇 You have been muted", + description=f"You have been muted in **{ctx.guild.name}**", + color=0xff0000, + timestamp=datetime.now() + ) + dm_embed.add_field(name="⏱️ Duration", value=duration, inline=True) + dm_embed.add_field(name="⏰ Ends At", value=f"", inline=True) + dm_embed.add_field(name="📝 Reason", value=reason, inline=False) + dm_embed.add_field(name="👮 Moderator", value=ctx.author.display_name, inline=True) + + if message_data and message_data['content']: + content_preview = message_data['content'][:200] + "..." if len(message_data['content']) > 200 else message_data['content'] + dm_embed.add_field(name="📄 Referenced Message", value=f"```{content_preview}```", inline=False) + + dm_embed.set_footer(text=f"Server: {ctx.guild.name}") + await user.send(embed=dm_embed) + except discord.Forbidden: + pass # User has DMs disabled # Log the action logger.info(f"User {user.id} muted by {ctx.author.id} in guild {ctx.guild.id} for {duration}. Reason: {reason}") except Exception as e: logger.error(f"Error in mute command: {e}") - await ctx.send("❌ Ein Fehler ist aufgetreten beim Muten des Benutzers.") + embed = discord.Embed( + title="❌ Error", + description="An error occurred while processing the mute. Please try again.", + color=0xff0000 + ) + await send_response(embed=embed) @client.hybrid_command() async def unmute(ctx, user: discord.User): @@ -4292,7 +4461,34 @@ async def addnotes(ctx, type: str, source: str = None, attachment: discord.Attac @client.hybrid_command() async def asknotes(ctx, *, question: str): """Asks a question about your saved notes.""" - await ctx.defer() + # Check if it's a slash command and defer if needed + is_slash_command = hasattr(ctx, 'interaction') and ctx.interaction + if is_slash_command: + await ctx.defer() + + # Helper function for sending responses + async def send_response(content=None, embed=None, ephemeral=False): + try: + if is_slash_command: + if embed: + await ctx.followup.send(embed=embed, ephemeral=ephemeral) + else: + await ctx.followup.send(content, ephemeral=ephemeral) + else: + if embed: + await ctx.send(embed=embed) + else: + await ctx.send(content) + except Exception as e: + logger.error(f"Error sending response: {e}") + # Fallback to regular send if followup fails + try: + if embed: + await ctx.send(embed=embed) + else: + await ctx.send(content) + except: + pass user_id = ctx.author.id guild_id = ctx.guild.id @@ -4301,7 +4497,7 @@ async def asknotes(ctx, *, question: str): asknotesintroduction = read_askintroduction() if not os.path.exists(note_file): - await ctx.send(f"No notes found for user {ctx.author.name}.") + await send_response(f"No notes found for user {ctx.author.name}.") return with open(note_file, "r", encoding="utf-8") as file: @@ -4317,11 +4513,38 @@ async def asknotes(ctx, *, question: str): # Erstelle ein Embed für die Bestätigungsnachricht embed = discord.Embed(title="Notes Query", color=0x00ff00) embed.add_field(name="Request Received", value="Your request has been added to the queue. Processing it now...") - await ctx.send(embed=embed) + await send_response(embed=embed) @client.hybrid_command() async def delnotes(ctx): """Deletes all saved notes and the asknotes history for the user.""" + # Check if it's a slash command and defer if needed + is_slash_command = hasattr(ctx, 'interaction') and ctx.interaction + + # Helper function for sending responses + async def send_response(content=None, embed=None, ephemeral=False): + try: + if is_slash_command: + if embed: + await ctx.followup.send(embed=embed, ephemeral=ephemeral) + else: + await ctx.followup.send(content, ephemeral=ephemeral) + else: + if embed: + await ctx.send(embed=embed) + else: + await ctx.send(content) + except Exception as e: + logger.error(f"Error sending response: {e}") + # Fallback to regular send if followup fails + try: + if embed: + await ctx.send(embed=embed) + else: + await ctx.send(content) + except: + pass + user_id = ctx.author.id guild_id = ctx.guild.id user_cache_dir = os.path.join(CACHE_DIR, f"{str(guild_id)}_{str(user_id)}") @@ -4333,11 +4556,11 @@ async def delnotes(ctx): # Setze die asknotes-Historie in der Datenbank zurück try: update_user_data(user_id, guild_id, "asknotes_history", None) - await ctx.send(f"All notes and asknotes history deleted for user {ctx.author.name}.") + await send_response(f"All notes and asknotes history deleted for user {ctx.author.name}.") except Exception as e: - await ctx.send(f"Error deleting asknotes history: {e}") + await send_response(f"Error deleting asknotes history: {e}") else: - await ctx.send(f"No notes found for user {ctx.author.name}.") + await send_response(f"No notes found for user {ctx.author.name}.") try: # Initialize database tables