modified: bot.py
This commit is contained in:
285
bot.py
285
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"<t:{int(end_time.timestamp())}: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"<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
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user