From 7012475a72cd4dd9cf30a5f5d3bf372eebd5a0db Mon Sep 17 00:00:00 2001 From: SimolZimol <70102430+SimolZimol@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:12:45 +0200 Subject: [PATCH] modified: bot.py --- bot.py | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 215 insertions(+), 27 deletions(-) diff --git a/bot.py b/bot.py index e82e5cd..e7dab89 100644 --- a/bot.py +++ b/bot.py @@ -915,8 +915,34 @@ async def handle_expired_mute(process_uuid, data): except Exception as e: logger.warning(f"Could not restore roles for user {user_id}: {e}") - # Send notification to channel if possible - if channel_id: + # Send notification to mod log channel (preferred) or original channel as fallback + notification_sent = False + + # Try to send to mod log channel first + try: + guild_settings = get_guild_settings(guild_id) + if guild_settings and guild_settings.get("mod_log_channel_id"): + mod_log_channel = guild.get_channel(int(guild_settings["mod_log_channel_id"])) + if mod_log_channel: + embed = discord.Embed( + title="๐Ÿ”Š User Automatically Unmuted", + description=f"{member.mention} has been automatically unmuted.", + color=0x00ff00, + timestamp=datetime.now() + ) + embed.add_field(name="๐Ÿ‘ค User", value=f"{member.mention}\n`{member.id}`", inline=True) + embed.add_field(name="๐Ÿ“ Reason", value="Mute duration expired", inline=True) + embed.add_field(name="๐Ÿ“ Original Channel", value=f"<#{channel_id}>" if channel_id else "Unknown", inline=True) + embed.set_footer(text=f"Process ID: {process_uuid}") + embed.set_thumbnail(url=member.display_avatar.url) + await mod_log_channel.send(embed=embed) + notification_sent = True + logger.info(f"Sent auto-unmute notification to mod log channel for user {user_id}") + except Exception as e: + logger.warning(f"Could not send unmute notification to mod log channel: {e}") + + # Fallback to original channel if mod log failed + if not notification_sent and channel_id: try: channel = guild.get_channel(int(channel_id)) if channel: @@ -929,8 +955,13 @@ async def handle_expired_mute(process_uuid, data): embed.add_field(name="๐Ÿ“ Reason", value="Mute duration expired", inline=False) embed.set_footer(text=f"Process ID: {process_uuid}") await channel.send(embed=embed) + notification_sent = True + logger.info(f"Sent auto-unmute notification to original channel for user {user_id}") except Exception as e: - logger.warning(f"Could not send unmute notification: {e}") + logger.warning(f"Could not send unmute notification to original channel: {e}") + + if not notification_sent: + logger.warning(f"No channel available for auto-unmute notification for user {user_id}") # Try to DM the user try: @@ -5498,26 +5529,63 @@ async def mute(ctx, user: discord.User, duration: str, reason: str = "No reason @client.hybrid_command() async def unmute(ctx, user: discord.User): - """Entmutet einen Benutzer manuell (Benรถtigt Permission Level 5 oder hรถher)""" + """Unmutes a user manually (Requires Permission Level 5 or higher)""" + # 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: + if hasattr(ctx, 'followup') and ctx.followup: + await ctx.followup.send(content=content, embed=embed, ephemeral=ephemeral, file=file) + elif hasattr(ctx, 'response') and not ctx.response.is_done(): + await ctx.response.send_message(content=content, embed=embed, ephemeral=ephemeral, file=file) + else: + await ctx.send(content=content, embed=embed, file=file) + else: + await ctx.send(content=content, embed=embed, file=file) + except Exception as e: + logger.error(f"Error sending response in unmute command: {e}") + try: + if embed: + await ctx.send(embed=embed) + elif content: + await ctx.send(content=content) + except Exception as fallback_error: + logger.error(f"Fallback send also failed: {fallback_error}") + try: - # Lade Moderator-Daten + # Load moderator data mod_data = await load_user_data(ctx.author.id, ctx.guild.id) - # รœberprรผfe Moderationsrechte + # Check moderation permissions if not check_moderation_permission(mod_data["permission"]): - await ctx.send("โŒ Du hast keine Berechtigung, diesen Befehl zu verwenden. (Benรถtigt Permission Level 5 oder hรถher)") + 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 - # 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) - # Finde Mute-Rolle basierend auf Einstellungen + # Find mute role based on settings mute_role = None if guild_settings["mute_role_id"]: mute_role = ctx.guild.get_role(guild_settings["mute_role_id"]) @@ -5526,40 +5594,160 @@ async def unmute(ctx, user: discord.User): mute_role = discord.utils.get(ctx.guild.roles, name=guild_settings["mute_role_name"]) if not mute_role or mute_role not in member.roles: - await ctx.send("โŒ Benutzer ist nicht gemutet.") + embed = discord.Embed( + title="โŒ User Not Muted", + description="This user is not currently muted.", + color=0xff0000 + ) + await send_response(embed=embed, ephemeral=True) return - # Entferne Mute-Rolle - await member.remove_roles(mute_role, reason=f"Unmuted by {ctx.author}") + # Find active mute records in user_mutes table + connection = None + cursor = None + active_mute_records = [] - # Stelle Rollen wieder her - await restore_user_roles(member, ctx.guild) + try: + connection = connect_to_database() + cursor = connection.cursor() + + # Get active mute records for this user + select_query = """ + SELECT id, process_uuid, reason, start_time, end_time, duration + FROM user_mutes + WHERE user_id = %s AND guild_id = %s AND aktiv = TRUE AND status = 'active' + ORDER BY start_time DESC + """ + + cursor.execute(select_query, (user.id, ctx.guild.id)) + results = cursor.fetchall() + + for result in results: + mute_id, process_uuid, reason, start_time, end_time, duration = result + active_mute_records.append({ + 'id': mute_id, + 'process_uuid': process_uuid, + 'reason': reason, + 'start_time': start_time, + 'end_time': end_time, + 'duration': duration + }) + + finally: + if cursor: + cursor.close() + if connection: + close_database_connection(connection) - # Finde und aktualisiere aktiven Mute-Prozess + if not active_mute_records: + embed = discord.Embed( + title="โŒ No Active Mutes Found", + description="No active mute records found for this user in the database.", + color=0xff0000 + ) + await send_response(embed=embed, ephemeral=True) + return + + # Remove mute role + await member.remove_roles(mute_role, reason=f"Manually unmuted by {ctx.author}") + + # Restore user roles + try: + restored_roles = await restore_user_roles(member, ctx.guild) + restored_count = len(restored_roles) if restored_roles else 0 + except Exception as e: + logger.warning(f"Could not restore roles for user {user.id}: {e}") + restored_count = 0 + + # Deactivate all active mute records + deactivated_mutes = [] + for mute_record in active_mute_records: + try: + await deactivate_mute(mute_record['id'], unmuted_by=ctx.author.id, auto_unmuted=False) + deactivated_mutes.append(mute_record) + except Exception as e: + logger.error(f"Error deactivating mute record {mute_record['id']}: {e}") + + # Cancel active processes + cancelled_processes = [] active_processes = get_active_processes(process_type="mute", guild_id=ctx.guild.id) for process in active_processes: if process["target_id"] == user.id: - update_process_status(process["uuid"], "cancelled_manual") - break + update_process_status(process["uuid"], "cancelled") + cancelled_processes.append(process["uuid"]) - # Erstelle Embed + # Create success embed embed = discord.Embed( - title="๐Ÿ”Š Benutzer entmutet", - description=f"{user.mention} wurde entmutet.", + title="๐Ÿ”Š User Unmuted Successfully", + description=f"{user.mention} has been manually unmuted.", color=0x00ff00, timestamp=datetime.now() ) - embed.add_field(name="Moderator", value=ctx.author.mention, inline=True) - embed.set_footer(text=f"User ID: {user.id}") - await ctx.send(embed=embed) + embed.add_field(name="๐Ÿ‘ฎ Moderator", value=ctx.author.mention, inline=True) + embed.add_field(name="๐Ÿ”“ Mutes Deactivated", value=str(len(deactivated_mutes)), inline=True) + + if restored_count > 0: + embed.add_field(name="๐ŸŽญ Roles Restored", value=str(restored_count), inline=True) + + # Show deactivated mute details + if deactivated_mutes: + mute_details = [] + for mute in deactivated_mutes[:3]: # Show first 3 mutes + mute_info = f"**ID {mute['id']}**: {mute['reason'][:30]}{'...' if len(mute['reason']) > 30 else ''}" + mute_info += f" ({mute['duration']})" + mute_details.append(mute_info) + + if len(deactivated_mutes) > 3: + mute_details.append(f"*+{len(deactivated_mutes) - 3} more mutes deactivated*") + + embed.add_field(name="๐Ÿ“‹ Deactivated Mutes", value="\n".join(mute_details), inline=False) + + embed.set_footer(text=f"User ID: {user.id} | {len(cancelled_processes)} process(es) cancelled") + embed.set_thumbnail(url=user.display_avatar.url) + + await send_response(embed=embed) # Log the action - logger.info(f"User {user.id} unmuted by {ctx.author.id} in guild {ctx.guild.id}") + log_additional_info = { + "Mutes Deactivated": str(len(deactivated_mutes)), + "Roles Restored": str(restored_count), + "Processes Cancelled": str(len(cancelled_processes)) + } + + await log_moderation_action( + guild=ctx.guild, + action_type="unmute", + moderator=ctx.author, + target_user=user, + reason="Manual unmute", + additional_info=log_additional_info + ) + + # Try to DM the user + try: + dm_embed = discord.Embed( + title="๐Ÿ”Š You have been unmuted", + description=f"Your mute in **{ctx.guild.name}** has been manually removed by a moderator.", + color=0x00ff00, + timestamp=datetime.now() + ) + dm_embed.add_field(name="๐Ÿ‘ฎ Unmuted by", value=ctx.author.mention, inline=True) + dm_embed.set_footer(text=f"Server: {ctx.guild.name}") + await user.send(embed=dm_embed) + except (discord.Forbidden, discord.NotFound): + pass # User has DMs disabled or doesn't exist + + logger.info(f"User {user.id} manually unmuted by {ctx.author.id} in guild {ctx.guild.id} - {len(deactivated_mutes)} mute(s) deactivated") except Exception as e: logger.error(f"Error in unmute command: {e}") - await ctx.send("โŒ Ein Fehler ist aufgetreten beim Entmuten des Benutzers.") + embed = discord.Embed( + title="โŒ Error", + description="An error occurred while unmuting the user.", + color=0xff0000 + ) + await send_response(embed=embed, ephemeral=True) @client.hybrid_command() async def listmutes(ctx, status: str = "active"):