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)
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user