modified: bot.py

This commit is contained in:
SimolZimol
2025-08-20 23:44:21 +02:00
parent a866544f7d
commit c05d179f20

285
bot.py
View File

@@ -3655,64 +3655,158 @@ async def viewwarn(ctx, warning_id: int):
await send_response(embed=embed) await send_response(embed=embed)
@client.hybrid_command() @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) """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: Duration examples:
- 10m = 10 minutes - 10m = 10 minutes
- 1h = 1 hour - 1h = 1 hour
- 2d = 2 days - 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: 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 # Load moderator data
mod_data = await load_user_data(ctx.author.id, ctx.guild.id) mod_data = await load_user_data(ctx.author.id, ctx.guild.id)
# Check moderation rights # Check moderation rights
if not check_moderation_permission(mod_data["permission"]): 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 return
# Parse duration # Parse duration
time_units = {'m': 60, 'h': 3600, 'd': 86400} time_units = {'m': 60, 'h': 3600, 'd': 86400}
if not duration[-1] in time_units or not duration[:-1].isdigit(): if not duration or not duration[-1] in time_units or not duration[:-1].isdigit():
await ctx.send("❌ Invalid time format. Use: 10m, 1h, 2d") embed = discord.Embed(
title="❌ Invalid Duration",
description="Invalid time format. Use: 10m, 1h, 2d",
color=0xff0000
)
await send_response(embed=embed, ephemeral=True)
return return
duration_seconds = int(duration[:-1]) * time_units[duration[-1]] duration_seconds = int(duration[:-1]) * time_units[duration[-1]]
end_time = datetime.now() + timedelta(seconds=duration_seconds) end_time = datetime.now() + timedelta(seconds=duration_seconds)
# Hole Member-Objekt # Get member object
member = ctx.guild.get_member(user.id) member = ctx.guild.get_member(user.id)
if not member: 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 return
# Lade Guild-Einstellungen # Load guild settings
guild_settings = get_guild_settings(ctx.guild.id) 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) 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()] roles_to_remove = [role for role in member.roles if not role.is_default()]
if roles_to_remove: if roles_to_remove:
await member.remove_roles(*roles_to_remove, reason=f"Muted by {ctx.author}") 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) mute_role = await get_or_create_mute_role(ctx.guild, guild_settings)
if not mute_role: 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 return
# Vergebe Mute-Rolle # Add mute role
await member.add_roles(mute_role, reason=f"Muted by {ctx.author} for {duration}") 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 = await load_user_data(user.id, ctx.guild.id)
user_data["mutes"] += 1 user_data["mutes"] += 1
update_user_data(user.id, ctx.guild.id, "mutes", user_data["mutes"]) 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 = { process_data = {
"user_id": user.id, "user_id": user.id,
"guild_id": ctx.guild.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 data=process_data
) )
# Erstelle Embed # Create embed
embed = discord.Embed( embed = discord.Embed(
title="🔇 Benutzer gemutet", title="🔇 User Muted",
description=f"{user.mention} wurde gemutet.", description=f"{user.mention} has been muted.",
color=0xff0000, color=0xff0000,
timestamp=datetime.now() timestamp=datetime.now()
) )
embed.add_field(name="Dauer", value=duration, inline=True) embed.add_field(name="⏱️ Duration", 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="Ends At", value=f"<t:{int(end_time.timestamp())}:F>", inline=True)
embed.add_field(name="Grund", value=reason, inline=False) embed.add_field(name="📝 Reason", value=reason, inline=False)
embed.add_field(name="Moderator", value=ctx.author.mention, inline=True) 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.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]}")
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"<t:{int(end_time.timestamp())}: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 # Log the action
logger.info(f"User {user.id} muted by {ctx.author.id} in guild {ctx.guild.id} for {duration}. Reason: {reason}") logger.info(f"User {user.id} muted by {ctx.author.id} in guild {ctx.guild.id} for {duration}. Reason: {reason}")
except Exception as e: except Exception as e:
logger.error(f"Error in mute command: {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() @client.hybrid_command()
async def unmute(ctx, user: discord.User): 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() @client.hybrid_command()
async def asknotes(ctx, *, question: str): async def asknotes(ctx, *, question: str):
"""Asks a question about your saved notes.""" """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 user_id = ctx.author.id
guild_id = ctx.guild.id guild_id = ctx.guild.id
@@ -4301,7 +4497,7 @@ async def asknotes(ctx, *, question: str):
asknotesintroduction = read_askintroduction() asknotesintroduction = read_askintroduction()
if not os.path.exists(note_file): 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 return
with open(note_file, "r", encoding="utf-8") as file: 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 # Erstelle ein Embed für die Bestätigungsnachricht
embed = discord.Embed(title="Notes Query", color=0x00ff00) 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...") 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() @client.hybrid_command()
async def delnotes(ctx): async def delnotes(ctx):
"""Deletes all saved notes and the asknotes history for the user.""" """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 user_id = ctx.author.id
guild_id = ctx.guild.id guild_id = ctx.guild.id
user_cache_dir = os.path.join(CACHE_DIR, f"{str(guild_id)}_{str(user_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 # Setze die asknotes-Historie in der Datenbank zurück
try: try:
update_user_data(user_id, guild_id, "asknotes_history", None) 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: except Exception as e:
await ctx.send(f"Error deleting asknotes history: {e}") await send_response(f"Error deleting asknotes history: {e}")
else: 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: try:
# Initialize database tables # Initialize database tables