modified: bot.py
This commit is contained in:
320
bot.py
320
bot.py
@@ -2587,6 +2587,122 @@ def check_moderation_permission(user_permission):
|
||||
"""Checks if the user has moderation rights (Permission 5 or higher)"""
|
||||
return user_permission >= 5
|
||||
|
||||
async def log_moderation_action(guild, action_type, moderator, target_user, reason, duration=None, additional_info=None):
|
||||
"""Logs moderation actions to the configured log channel"""
|
||||
try:
|
||||
guild_settings = get_guild_settings(guild.id)
|
||||
|
||||
# Check if logging is enabled and channel is configured
|
||||
if not guild_settings["mod_log_enabled"] or not guild_settings["log_channel_id"]:
|
||||
return
|
||||
|
||||
log_channel = guild.get_channel(guild_settings["log_channel_id"])
|
||||
if not log_channel:
|
||||
logger.warning(f"Log channel {guild_settings['log_channel_id']} not found in guild {guild.id}")
|
||||
return
|
||||
|
||||
# Create log embed
|
||||
color_map = {
|
||||
"warn": 0xff9500,
|
||||
"mute": 0xff0000,
|
||||
"unmute": 0x00ff00,
|
||||
"kick": 0xff6600,
|
||||
"ban": 0x8b0000,
|
||||
"unban": 0x00ff00
|
||||
}
|
||||
|
||||
embed = discord.Embed(
|
||||
title=f"🛡️ Moderation Action: {action_type.title()}",
|
||||
color=color_map.get(action_type.lower(), 0x3498db),
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
|
||||
embed.add_field(name="👤 Target User", value=f"{target_user.mention}\n`{target_user.id}`", inline=True)
|
||||
embed.add_field(name="👮 Moderator", value=f"{moderator.mention}\n`{moderator.id}`", inline=True)
|
||||
embed.add_field(name="📝 Reason", value=reason, inline=True)
|
||||
|
||||
if duration:
|
||||
embed.add_field(name="⏱️ Duration", value=duration, inline=True)
|
||||
|
||||
if additional_info:
|
||||
for key, value in additional_info.items():
|
||||
embed.add_field(name=key, value=value, inline=True)
|
||||
|
||||
embed.set_thumbnail(url=target_user.display_avatar.url)
|
||||
embed.set_footer(text=f"Action ID: {guild.id}-{target_user.id}-{int(datetime.now().timestamp())}")
|
||||
|
||||
await log_channel.send(embed=embed)
|
||||
logger.info(f"Logged {action_type} action for user {target_user.id} in guild {guild.id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error logging moderation action: {e}")
|
||||
|
||||
async def save_warning_to_database(user_id, guild_id, moderator_id, reason, timestamp=None):
|
||||
"""Saves individual warning records to the database"""
|
||||
connection = None
|
||||
cursor = None
|
||||
try:
|
||||
connection = connect_to_database()
|
||||
cursor = connection.cursor()
|
||||
|
||||
if timestamp is None:
|
||||
timestamp = datetime.now()
|
||||
|
||||
insert_query = """
|
||||
INSERT INTO user_warnings (user_id, guild_id, moderator_id, reason, created_at)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
"""
|
||||
|
||||
cursor.execute(insert_query, (user_id, guild_id, moderator_id, reason, timestamp))
|
||||
connection.commit()
|
||||
|
||||
logger.info(f"Saved warning record for user {user_id} in guild {guild_id}")
|
||||
return cursor.lastrowid
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving warning to database: {e}")
|
||||
if connection:
|
||||
connection.rollback()
|
||||
return None
|
||||
finally:
|
||||
if cursor:
|
||||
cursor.close()
|
||||
if connection:
|
||||
close_database_connection(connection)
|
||||
|
||||
def create_warnings_table():
|
||||
"""Creates the user_warnings table if it doesn't exist"""
|
||||
connection = None
|
||||
cursor = None
|
||||
try:
|
||||
connection = connect_to_database()
|
||||
cursor = connection.cursor()
|
||||
|
||||
create_table_query = """
|
||||
CREATE TABLE IF NOT EXISTS user_warnings (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL,
|
||||
guild_id BIGINT NOT NULL,
|
||||
moderator_id BIGINT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_guild (user_id, guild_id),
|
||||
INDEX idx_created_at (created_at)
|
||||
)
|
||||
"""
|
||||
|
||||
cursor.execute(create_table_query)
|
||||
connection.commit()
|
||||
logger.info("User warnings table checked/created successfully")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating warnings table: {e}")
|
||||
finally:
|
||||
if cursor:
|
||||
cursor.close()
|
||||
if connection:
|
||||
close_database_connection(connection)
|
||||
|
||||
async def save_user_roles(user_id, guild_id, roles):
|
||||
"""Saves a user's roles before a mute"""
|
||||
connection = None
|
||||
@@ -2669,46 +2785,157 @@ async def warn(ctx, user: discord.User, *, reason: str = "No reason provided"):
|
||||
|
||||
# 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 ctx.send(embed=embed, ephemeral=True)
|
||||
return
|
||||
|
||||
# Cannot warn yourself
|
||||
if user.id == ctx.author.id:
|
||||
embed = discord.Embed(
|
||||
title="❌ Invalid Action",
|
||||
description="You cannot warn yourself!",
|
||||
color=0xff0000
|
||||
)
|
||||
await ctx.send(embed=embed, ephemeral=True)
|
||||
return
|
||||
|
||||
# Check if target has higher permissions
|
||||
target_data = await load_user_data(user.id, ctx.guild.id)
|
||||
|
||||
if target_data["permission"] >= mod_data["permission"]:
|
||||
embed = discord.Embed(
|
||||
title="❌ Insufficient Permissions",
|
||||
description="You cannot warn someone with equal or higher permissions than you.",
|
||||
color=0xff0000
|
||||
)
|
||||
await ctx.send(embed=embed, ephemeral=True)
|
||||
return
|
||||
|
||||
# Load user data
|
||||
user_data = await load_user_data(user.id, ctx.guild.id)
|
||||
|
||||
# Increase warn count
|
||||
user_data["warns"] += 1
|
||||
update_user_data(user.id, ctx.guild.id, "warns", user_data["warns"])
|
||||
target_data["warns"] += 1
|
||||
update_user_data(user.id, ctx.guild.id, "warns", target_data["warns"])
|
||||
|
||||
# Save detailed warning record to database
|
||||
warning_id = await save_warning_to_database(
|
||||
user_id=user.id,
|
||||
guild_id=ctx.guild.id,
|
||||
moderator_id=ctx.author.id,
|
||||
reason=reason
|
||||
)
|
||||
|
||||
# Get guild settings for threshold checking
|
||||
guild_settings = get_guild_settings(ctx.guild.id)
|
||||
warn_threshold = guild_settings.get("max_warn_threshold", 3)
|
||||
|
||||
# Create embed
|
||||
embed = discord.Embed(
|
||||
title="⚠️ Warning issued",
|
||||
title="⚠️ Warning Issued",
|
||||
description=f"{user.mention} has been warned.",
|
||||
color=0xff9500,
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
embed.add_field(name="Reason", value=reason, inline=False)
|
||||
embed.add_field(name="Moderator", value=ctx.author.mention, inline=True)
|
||||
embed.add_field(name="Warning Count", value=f"{user_data['warns']}", 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="⚠️ Warning Count", value=f"{target_data['warns']}/{warn_threshold}", inline=True)
|
||||
|
||||
if warning_id:
|
||||
embed.add_field(name="🆔 Warning ID", value=str(warning_id), inline=True)
|
||||
|
||||
embed.set_footer(text=f"User ID: {user.id}")
|
||||
embed.set_thumbnail(url=user.display_avatar.url)
|
||||
|
||||
# Check if user has reached the warning threshold
|
||||
if target_data['warns'] >= warn_threshold:
|
||||
auto_mute_enabled = guild_settings.get("auto_mute_on_warns", False)
|
||||
if auto_mute_enabled:
|
||||
mute_role_id = guild_settings.get("mute_role_id")
|
||||
if mute_role_id and ctx.guild.get_role(mute_role_id):
|
||||
mute_role = ctx.guild.get_role(mute_role_id)
|
||||
try:
|
||||
member = ctx.guild.get_member(user.id)
|
||||
if member:
|
||||
await member.add_roles(mute_role, reason=f"Automatic mute: {warn_threshold} warnings reached")
|
||||
embed.add_field(
|
||||
name="🔇 Automatic Action",
|
||||
value=f"User has been automatically muted for reaching {warn_threshold} warnings.",
|
||||
inline=False
|
||||
)
|
||||
|
||||
# Log the automatic mute action
|
||||
await log_moderation_action(
|
||||
guild=ctx.guild,
|
||||
action_type="mute",
|
||||
moderator=ctx.author,
|
||||
target_user=user,
|
||||
reason=f"Automatic mute: {warn_threshold} warnings reached",
|
||||
additional_info={"Trigger": f"Warning #{target_data['warns']}"}
|
||||
)
|
||||
except discord.Forbidden:
|
||||
embed.add_field(
|
||||
name="⚠️ Warning",
|
||||
value="Could not automatically mute user due to insufficient permissions.",
|
||||
inline=False
|
||||
)
|
||||
else:
|
||||
embed.add_field(
|
||||
name="🚨 Threshold Reached",
|
||||
value=f"User has reached the warning threshold ({warn_threshold} warnings). Consider further action.",
|
||||
inline=False
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
# Log the warning action
|
||||
await log_moderation_action(
|
||||
guild=ctx.guild,
|
||||
action_type="warn",
|
||||
moderator=ctx.author,
|
||||
target_user=user,
|
||||
reason=reason,
|
||||
additional_info={
|
||||
"Warning Count": f"{target_data['warns']}/{warn_threshold}",
|
||||
"Warning ID": str(warning_id) if warning_id else "N/A"
|
||||
}
|
||||
)
|
||||
|
||||
# Try to DM the user
|
||||
try:
|
||||
dm_embed = discord.Embed(
|
||||
title=f"⚠️ Warning from {ctx.guild.name}",
|
||||
color=0xff9500,
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
dm_embed.add_field(name="👮 Moderator", value=ctx.author.display_name, inline=True)
|
||||
dm_embed.add_field(name="📝 Reason", value=reason, inline=False)
|
||||
dm_embed.add_field(name="⚠️ Total Warnings", value=f"{target_data['warns']}/{warn_threshold}", inline=True)
|
||||
|
||||
if target_data['warns'] >= warn_threshold:
|
||||
dm_embed.add_field(
|
||||
name="🔇 Additional Action",
|
||||
value="You have reached the warning threshold. Further violations may result in more severe punishments.",
|
||||
inline=False
|
||||
)
|
||||
|
||||
await user.send(embed=dm_embed)
|
||||
except discord.Forbidden:
|
||||
# User has DMs disabled
|
||||
pass
|
||||
|
||||
# Log the action
|
||||
logger.info(f"User {user.id} warned by {ctx.author.id} in guild {ctx.guild.id}. Reason: {reason}")
|
||||
|
||||
# Auto-actions based on warning count
|
||||
if user_data["warns"] >= 3:
|
||||
embed_auto = discord.Embed(
|
||||
title="🚨 Auto-action triggered",
|
||||
description=f"{user.mention} has reached {user_data['warns']} warnings!",
|
||||
color=0xff0000
|
||||
)
|
||||
embed_auto.add_field(name="Recommendation", value="Consider further moderation measures", inline=False)
|
||||
await ctx.send(embed=embed_auto)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in warn command: {e}")
|
||||
await ctx.send("❌ An error occurred while warning the user.")
|
||||
embed = discord.Embed(
|
||||
title="❌ Error",
|
||||
description="An error occurred while processing the warning. Please try again.",
|
||||
color=0xff0000
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@client.hybrid_command()
|
||||
async def mywarns(ctx):
|
||||
@@ -2723,11 +2950,18 @@ async def mywarns(ctx):
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
|
||||
# Warning information
|
||||
warn_color = "🟢" if user_data["warns"] == 0 else "🟡" if user_data["warns"] < 3 else "🔴"
|
||||
# Get guild settings for thresholds
|
||||
guild_settings = get_guild_settings(ctx.guild.id)
|
||||
|
||||
# Get detailed warning records
|
||||
warning_records = await get_user_warnings(ctx.author.id, ctx.guild.id)
|
||||
|
||||
# Warning information with threshold
|
||||
warn_threshold = guild_settings.get("max_warn_threshold", 3)
|
||||
warn_color = "🟢" if user_data["warns"] == 0 else "🟡" if user_data["warns"] < warn_threshold else "🔴"
|
||||
embed.add_field(
|
||||
name=f"{warn_color} Warnings",
|
||||
value=f"**{user_data['warns']}** warning(s)",
|
||||
value=f"**{user_data['warns']}/{warn_threshold}** warnings",
|
||||
inline=True
|
||||
)
|
||||
|
||||
@@ -2765,11 +2999,37 @@ async def mywarns(ctx):
|
||||
embed.add_field(name="📈 Status", value=status, inline=False)
|
||||
embed.color = status_color
|
||||
|
||||
# Detailed warning history (show last 5 warnings)
|
||||
if warning_records:
|
||||
warning_history = "**Recent Warning History:**\n"
|
||||
display_count = min(5, len(warning_records))
|
||||
|
||||
for i in range(display_count):
|
||||
record = warning_records[i]
|
||||
moderator = ctx.guild.get_member(record["moderator_id"])
|
||||
mod_name = moderator.display_name if moderator else f"ID: {record['moderator_id']}"
|
||||
|
||||
# Format date
|
||||
warning_date = record["created_at"].strftime("%d.%m.%Y %H:%M")
|
||||
|
||||
# Truncate reason if too long
|
||||
reason = record["reason"]
|
||||
if len(reason) > 50:
|
||||
reason = reason[:47] + "..."
|
||||
|
||||
warning_history += f"`{warning_date}` - **{mod_name}**: {reason}\n"
|
||||
|
||||
if len(warning_records) > 5:
|
||||
warning_history += f"\n*... and {len(warning_records) - 5} more warning(s)*"
|
||||
|
||||
embed.add_field(name="📋 Warning Details", value=warning_history, inline=False)
|
||||
|
||||
# Get guild settings for thresholds
|
||||
guild_settings = get_guild_settings(ctx.guild.id)
|
||||
threshold_info = f"Warning threshold: **{guild_settings['max_warn_threshold']}** warnings"
|
||||
if guild_settings["auto_mute_on_warns"]:
|
||||
threshold_info += f"\nAuto-mute duration: **{guild_settings['auto_mute_duration']}**"
|
||||
threshold_info = f"Warning threshold: **{warn_threshold}** warnings"
|
||||
auto_mute_enabled = guild_settings.get("auto_mute_on_warns", False)
|
||||
if auto_mute_enabled:
|
||||
auto_mute_duration = guild_settings.get("auto_mute_duration", "1 hour")
|
||||
threshold_info += f"\nAuto-mute: **{auto_mute_duration}** (at {warn_threshold} warnings)"
|
||||
|
||||
embed.add_field(name="⚙️ Server Settings", value=threshold_info, inline=False)
|
||||
|
||||
@@ -3412,6 +3672,10 @@ async def delnotes(ctx):
|
||||
await ctx.send(f"No notes found for user {ctx.author.name}.")
|
||||
|
||||
try:
|
||||
# Initialize database tables
|
||||
create_warnings_table()
|
||||
logger.info("Database tables initialized successfully")
|
||||
|
||||
loop.run_until_complete(client.start(TOKEN))
|
||||
except KeyboardInterrupt:
|
||||
loop.run_until_complete(client.logout())
|
||||
|
||||
Reference in New Issue
Block a user