modified: bot.py
This commit is contained in:
397
bot.py
397
bot.py
@@ -1991,10 +1991,11 @@ async def modhelp(ctx):
|
|||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="👮 Moderator Commands (Level 5+)",
|
name="👮 Moderator Commands (Level 5+)",
|
||||||
value=(
|
value=(
|
||||||
"`/warn <user> [reason]` - Warn a user\n"
|
"`/warn <user> [reason | message_id]` - Warn a user (with optional message reference)\n"
|
||||||
"`/mute <user> <duration> [reason]` - Mute a user temporarily\n"
|
"`/mute <user> <duration> [reason]` - Mute a user temporarily\n"
|
||||||
"`/unmute <user>` - Manually unmute a user\n"
|
"`/unmute <user>` - Manually unmute a user\n"
|
||||||
"`/modinfo [user]` - View comprehensive user information\n"
|
"`/modinfo [user]` - View comprehensive user information\n"
|
||||||
|
"`/viewwarn <warning_id>` - View detailed warning information\n"
|
||||||
"`/modstats [user]` - View moderation statistics\n"
|
"`/modstats [user]` - View moderation statistics\n"
|
||||||
"`/processes <action> [type]` - Manage active processes\n"
|
"`/processes <action> [type]` - Manage active processes\n"
|
||||||
"`/startgiveaway` - Create server giveaways\n"
|
"`/startgiveaway` - Create server giveaways\n"
|
||||||
@@ -2046,12 +2047,13 @@ async def modhelp(ctx):
|
|||||||
|
|
||||||
# Duration Formats
|
# Duration Formats
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="⏱️ Duration Formats",
|
name="⏱️ Command Examples",
|
||||||
value=(
|
value=(
|
||||||
"When using mute commands:\n"
|
"**Mute duration formats:**\n"
|
||||||
"`10m` = 10 minutes\n"
|
"`10m` = 10 minutes, `1h` = 1 hour, `2d` = 2 days\n\n"
|
||||||
"`1h` = 1 hour\n"
|
"**Warning with message reference:**\n"
|
||||||
"`2d` = 2 days"
|
"`/warn @user Inappropriate behavior | 1234567890123456789`\n"
|
||||||
|
"The message will be saved even if deleted later."
|
||||||
),
|
),
|
||||||
inline=False
|
inline=False
|
||||||
)
|
)
|
||||||
@@ -2638,8 +2640,8 @@ async def log_moderation_action(guild, action_type, moderator, target_user, reas
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error logging moderation action: {e}")
|
logger.error(f"Error logging moderation action: {e}")
|
||||||
|
|
||||||
async def save_warning_to_database(user_id, guild_id, moderator_id, reason, timestamp=None):
|
async def save_warning_to_database(user_id, guild_id, moderator_id, reason, timestamp=None, message_data=None):
|
||||||
"""Saves individual warning records to the database"""
|
"""Saves individual warning records to the database with optional message data"""
|
||||||
connection = None
|
connection = None
|
||||||
cursor = None
|
cursor = None
|
||||||
try:
|
try:
|
||||||
@@ -2649,12 +2651,32 @@ async def save_warning_to_database(user_id, guild_id, moderator_id, reason, time
|
|||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.now()
|
timestamp = datetime.now()
|
||||||
|
|
||||||
|
# Prepare message data if provided
|
||||||
|
message_id = None
|
||||||
|
message_content = None
|
||||||
|
message_attachments = None
|
||||||
|
message_author_id = None
|
||||||
|
message_channel_id = None
|
||||||
|
|
||||||
|
if message_data:
|
||||||
|
message_id = message_data.get('id')
|
||||||
|
message_content = message_data.get('content')
|
||||||
|
message_attachments = message_data.get('attachments') # JSON string
|
||||||
|
message_author_id = message_data.get('author_id')
|
||||||
|
message_channel_id = message_data.get('channel_id')
|
||||||
|
|
||||||
insert_query = """
|
insert_query = """
|
||||||
INSERT INTO user_warnings (user_id, guild_id, moderator_id, reason, created_at)
|
INSERT INTO user_warnings (user_id, guild_id, moderator_id, reason, message_id,
|
||||||
VALUES (%s, %s, %s, %s, %s)
|
message_content, message_attachments, message_author_id,
|
||||||
|
message_channel_id, created_at)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cursor.execute(insert_query, (user_id, guild_id, moderator_id, reason, timestamp))
|
cursor.execute(insert_query, (
|
||||||
|
user_id, guild_id, moderator_id, reason, message_id,
|
||||||
|
message_content, message_attachments, message_author_id,
|
||||||
|
message_channel_id, timestamp
|
||||||
|
))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
logger.info(f"Saved warning record for user {user_id} in guild {guild_id}")
|
logger.info(f"Saved warning record for user {user_id} in guild {guild_id}")
|
||||||
@@ -2686,13 +2708,37 @@ def create_warnings_table():
|
|||||||
guild_id BIGINT NOT NULL,
|
guild_id BIGINT NOT NULL,
|
||||||
moderator_id BIGINT NOT NULL,
|
moderator_id BIGINT NOT NULL,
|
||||||
reason TEXT NOT NULL,
|
reason TEXT NOT NULL,
|
||||||
|
message_id BIGINT NULL,
|
||||||
|
message_content LONGTEXT NULL,
|
||||||
|
message_attachments LONGTEXT NULL,
|
||||||
|
message_author_id BIGINT NULL,
|
||||||
|
message_channel_id BIGINT NULL,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
INDEX idx_user_guild (user_id, guild_id),
|
INDEX idx_user_guild (user_id, guild_id),
|
||||||
INDEX idx_created_at (created_at)
|
INDEX idx_created_at (created_at),
|
||||||
|
INDEX idx_message_id (message_id)
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cursor.execute(create_table_query)
|
cursor.execute(create_table_query)
|
||||||
|
|
||||||
|
# Add new columns if they don't exist (for existing databases)
|
||||||
|
alter_queries = [
|
||||||
|
"ALTER TABLE user_warnings ADD COLUMN message_id BIGINT NULL",
|
||||||
|
"ALTER TABLE user_warnings ADD COLUMN message_content LONGTEXT NULL",
|
||||||
|
"ALTER TABLE user_warnings ADD COLUMN message_attachments LONGTEXT NULL",
|
||||||
|
"ALTER TABLE user_warnings ADD COLUMN message_author_id BIGINT NULL",
|
||||||
|
"ALTER TABLE user_warnings ADD COLUMN message_channel_id BIGINT NULL",
|
||||||
|
"ALTER TABLE user_warnings ADD INDEX idx_message_id (message_id)"
|
||||||
|
]
|
||||||
|
|
||||||
|
for alter_query in alter_queries:
|
||||||
|
try:
|
||||||
|
cursor.execute(alter_query)
|
||||||
|
except Exception:
|
||||||
|
# Column already exists, ignore error
|
||||||
|
pass
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
logger.info("User warnings table checked/created successfully")
|
logger.info("User warnings table checked/created successfully")
|
||||||
|
|
||||||
@@ -2713,7 +2759,8 @@ async def get_user_warnings(user_id, guild_id):
|
|||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
select_query = """
|
select_query = """
|
||||||
SELECT id, moderator_id, reason, created_at
|
SELECT id, moderator_id, reason, created_at, message_id, message_content,
|
||||||
|
message_attachments, message_author_id, message_channel_id
|
||||||
FROM user_warnings
|
FROM user_warnings
|
||||||
WHERE user_id = %s AND guild_id = %s
|
WHERE user_id = %s AND guild_id = %s
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
@@ -2728,7 +2775,12 @@ async def get_user_warnings(user_id, guild_id):
|
|||||||
"id": row[0],
|
"id": row[0],
|
||||||
"moderator_id": row[1],
|
"moderator_id": row[1],
|
||||||
"reason": row[2],
|
"reason": row[2],
|
||||||
"created_at": row[3]
|
"created_at": row[3],
|
||||||
|
"message_id": row[4],
|
||||||
|
"message_content": row[5],
|
||||||
|
"message_attachments": row[6],
|
||||||
|
"message_author_id": row[7],
|
||||||
|
"message_channel_id": row[8]
|
||||||
})
|
})
|
||||||
|
|
||||||
return warnings
|
return warnings
|
||||||
@@ -2742,6 +2794,75 @@ async def get_user_warnings(user_id, guild_id):
|
|||||||
if connection:
|
if connection:
|
||||||
close_database_connection(connection)
|
close_database_connection(connection)
|
||||||
|
|
||||||
|
async def get_message_data(channel, message_id):
|
||||||
|
"""Retrieves and processes message data for warning documentation"""
|
||||||
|
try:
|
||||||
|
message = await channel.fetch_message(message_id)
|
||||||
|
|
||||||
|
# Process attachments
|
||||||
|
attachments_data = []
|
||||||
|
for attachment in message.attachments:
|
||||||
|
attachment_info = {
|
||||||
|
"filename": attachment.filename,
|
||||||
|
"url": attachment.url,
|
||||||
|
"proxy_url": attachment.proxy_url,
|
||||||
|
"size": attachment.size,
|
||||||
|
"content_type": attachment.content_type
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download and encode image attachments for permanent storage
|
||||||
|
if attachment.content_type and attachment.content_type.startswith('image/'):
|
||||||
|
try:
|
||||||
|
import aiohttp
|
||||||
|
import base64
|
||||||
|
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(attachment.url) as response:
|
||||||
|
if response.status == 200 and len(await response.read()) < 8 * 1024 * 1024: # Max 8MB
|
||||||
|
image_data = await response.read()
|
||||||
|
attachment_info["data"] = base64.b64encode(image_data).decode('utf-8')
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not download attachment {attachment.filename}: {e}")
|
||||||
|
|
||||||
|
attachments_data.append(attachment_info)
|
||||||
|
|
||||||
|
# Process embeds
|
||||||
|
embeds_data = []
|
||||||
|
for embed in message.embeds:
|
||||||
|
embed_info = {
|
||||||
|
"title": embed.title,
|
||||||
|
"description": embed.description,
|
||||||
|
"url": embed.url,
|
||||||
|
"color": embed.color.value if embed.color else None,
|
||||||
|
"timestamp": embed.timestamp.isoformat() if embed.timestamp else None
|
||||||
|
}
|
||||||
|
embeds_data.append(embed_info)
|
||||||
|
|
||||||
|
message_data = {
|
||||||
|
"id": message.id,
|
||||||
|
"content": message.content,
|
||||||
|
"author_id": message.author.id,
|
||||||
|
"channel_id": message.channel.id,
|
||||||
|
"attachments": json.dumps(attachments_data) if attachments_data else None,
|
||||||
|
"embeds": json.dumps(embeds_data) if embeds_data else None,
|
||||||
|
"created_at": message.created_at.isoformat(),
|
||||||
|
"edited_at": message.edited_at.isoformat() if message.edited_at else None,
|
||||||
|
"message_type": str(message.type),
|
||||||
|
"flags": message.flags.value if message.flags else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return message_data
|
||||||
|
|
||||||
|
except discord.NotFound:
|
||||||
|
logger.warning(f"Message {message_id} not found")
|
||||||
|
return None
|
||||||
|
except discord.Forbidden:
|
||||||
|
logger.warning(f"No permission to access message {message_id}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error retrieving message data: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
async def save_user_roles(user_id, guild_id, roles):
|
async def save_user_roles(user_id, guild_id, roles):
|
||||||
"""Saves a user's roles before a mute"""
|
"""Saves a user's roles before a mute"""
|
||||||
connection = None
|
connection = None
|
||||||
@@ -2816,9 +2937,42 @@ async def restore_user_roles(user, guild):
|
|||||||
close_database_connection(connection)
|
close_database_connection(connection)
|
||||||
|
|
||||||
@client.hybrid_command()
|
@client.hybrid_command()
|
||||||
async def warn(ctx, user: discord.User, *, reason: str = "No reason provided"):
|
async def warn(ctx, user: discord.User, *, reason_and_message: str = "No reason provided"):
|
||||||
"""Warns a user (Requires Permission Level 5 or higher)"""
|
"""Warns a user (Requires Permission Level 5 or higher)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
/warn @user Inappropriate behavior
|
||||||
|
/warn @user Spam messages | 1234567890123456789
|
||||||
|
|
||||||
|
Format: reason | message_id (optional)
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
|
# Parse reason and optional message ID
|
||||||
|
reason_parts = reason_and_message.split(" | ")
|
||||||
|
reason = reason_parts[0].strip()
|
||||||
|
message_id = None
|
||||||
|
message_data = None
|
||||||
|
|
||||||
|
if len(reason_parts) > 1:
|
||||||
|
try:
|
||||||
|
message_id = int(reason_parts[1].strip())
|
||||||
|
# Try to get message data from current channel first
|
||||||
|
message_data = await get_message_data(ctx.channel, message_id)
|
||||||
|
|
||||||
|
# If not found in current channel, try other channels the bot can access
|
||||||
|
if message_data is None:
|
||||||
|
for channel in ctx.guild.text_channels:
|
||||||
|
try:
|
||||||
|
message_data = await get_message_data(channel, message_id)
|
||||||
|
if message_data is not None:
|
||||||
|
break
|
||||||
|
except discord.Forbidden:
|
||||||
|
continue
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
await ctx.send("❌ Invalid message ID format. Use: `/warn @user reason | 1234567890123456789`", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
||||||
@@ -2863,7 +3017,8 @@ async def warn(ctx, user: discord.User, *, reason: str = "No reason provided"):
|
|||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
guild_id=ctx.guild.id,
|
guild_id=ctx.guild.id,
|
||||||
moderator_id=ctx.author.id,
|
moderator_id=ctx.author.id,
|
||||||
reason=reason
|
reason=reason,
|
||||||
|
message_data=message_data
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get guild settings for threshold checking
|
# Get guild settings for threshold checking
|
||||||
@@ -2884,6 +3039,34 @@ async def warn(ctx, user: discord.User, *, reason: str = "No reason provided"):
|
|||||||
if warning_id:
|
if warning_id:
|
||||||
embed.add_field(name="🆔 Warning ID", value=str(warning_id), inline=True)
|
embed.add_field(name="🆔 Warning ID", value=str(warning_id), inline=True)
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
if message_data['content']:
|
||||||
|
content_preview = message_data['content']
|
||||||
|
if len(content_preview) > 100:
|
||||||
|
content_preview = content_preview[:97] + "..."
|
||||||
|
message_info += f"**Content:** {content_preview}\n"
|
||||||
|
|
||||||
|
# Show attachment info
|
||||||
|
if message_data['attachments']:
|
||||||
|
attachments = json.loads(message_data['attachments'])
|
||||||
|
if attachments:
|
||||||
|
attachment_names = [att['filename'] for att in attachments[:3]]
|
||||||
|
message_info += f"**Attachments:** {', '.join(attachment_names)}"
|
||||||
|
if len(attachments) > 3:
|
||||||
|
message_info += f" +{len(attachments) - 3} more"
|
||||||
|
|
||||||
|
embed.add_field(name="📄 Referenced Message", value=message_info, inline=False)
|
||||||
|
elif message_id:
|
||||||
|
embed.add_field(
|
||||||
|
name="⚠️ Message Not Found",
|
||||||
|
value=f"Could not retrieve message `{message_id}` (deleted or no permission)",
|
||||||
|
inline=False
|
||||||
|
)
|
||||||
|
|
||||||
embed.set_footer(text=f"User ID: {user.id}")
|
embed.set_footer(text=f"User ID: {user.id}")
|
||||||
embed.set_thumbnail(url=user.display_avatar.url)
|
embed.set_thumbnail(url=user.display_avatar.url)
|
||||||
|
|
||||||
@@ -2929,16 +3112,25 @@ async def warn(ctx, user: discord.User, *, reason: str = "No reason provided"):
|
|||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
# Log the warning action
|
# Log the warning action
|
||||||
|
log_additional_info = {
|
||||||
|
"Warning Count": f"{target_data['warns']}/{warn_threshold}",
|
||||||
|
"Warning ID": str(warning_id) if warning_id else "N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
if message_data:
|
||||||
|
log_additional_info["Referenced Message"] = f"ID: {message_data['id']}"
|
||||||
|
log_additional_info["Message Channel"] = f"<#{message_data['channel_id']}>"
|
||||||
|
if message_data['content']:
|
||||||
|
content_preview = message_data['content'][:200] + "..." if len(message_data['content']) > 200 else message_data['content']
|
||||||
|
log_additional_info["Message Content"] = content_preview
|
||||||
|
|
||||||
await log_moderation_action(
|
await log_moderation_action(
|
||||||
guild=ctx.guild,
|
guild=ctx.guild,
|
||||||
action_type="warn",
|
action_type="warn",
|
||||||
moderator=ctx.author,
|
moderator=ctx.author,
|
||||||
target_user=user,
|
target_user=user,
|
||||||
reason=reason,
|
reason=reason,
|
||||||
additional_info={
|
additional_info=log_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 to DM the user
|
||||||
@@ -3053,10 +3245,16 @@ async def mywarns(ctx):
|
|||||||
|
|
||||||
# Truncate reason if too long
|
# Truncate reason if too long
|
||||||
reason = record["reason"]
|
reason = record["reason"]
|
||||||
if len(reason) > 50:
|
if len(reason) > 40:
|
||||||
reason = reason[:47] + "..."
|
reason = reason[:37] + "..."
|
||||||
|
|
||||||
warning_history += f"`{warning_date}` - **{mod_name}**: {reason}\n"
|
warning_line = f"`{warning_date}` **{mod_name}**: {reason}"
|
||||||
|
|
||||||
|
# Add message indicator if warning was linked to a message
|
||||||
|
if record.get("message_id"):
|
||||||
|
warning_line += " 📄"
|
||||||
|
|
||||||
|
warning_history += warning_line + "\n"
|
||||||
|
|
||||||
if len(warning_records) > 5:
|
if len(warning_records) > 5:
|
||||||
warning_history += f"\n*... and {len(warning_records) - 5} more warning(s)*"
|
warning_history += f"\n*... and {len(warning_records) - 5} more warning(s)*"
|
||||||
@@ -3218,10 +3416,19 @@ async def modinfo(ctx, user: discord.User = None):
|
|||||||
|
|
||||||
# Truncate reason if too long
|
# Truncate reason if too long
|
||||||
reason = record["reason"]
|
reason = record["reason"]
|
||||||
if len(reason) > 60:
|
if len(reason) > 50:
|
||||||
reason = reason[:57] + "..."
|
reason = reason[:47] + "..."
|
||||||
|
|
||||||
warning_history += f"`{warning_date}` **{mod_name}**: {reason}\n"
|
warning_line = f"`{warning_date}` **{mod_name}**: {reason}"
|
||||||
|
|
||||||
|
# Add message indicator and content preview if available
|
||||||
|
if record.get("message_id"):
|
||||||
|
warning_line += " 📄"
|
||||||
|
if record.get("message_content"):
|
||||||
|
content_preview = record["message_content"][:30] + "..." if len(record["message_content"]) > 30 else record["message_content"]
|
||||||
|
warning_line += f"\n *Message: {content_preview}*"
|
||||||
|
|
||||||
|
warning_history += warning_line + "\n"
|
||||||
|
|
||||||
if len(warning_records) > 3:
|
if len(warning_records) > 3:
|
||||||
warning_history += f"*... and {len(warning_records) - 3} more warning(s)*"
|
warning_history += f"*... and {len(warning_records) - 3} more warning(s)*"
|
||||||
@@ -3287,6 +3494,142 @@ async def modinfo(ctx, user: discord.User = None):
|
|||||||
)
|
)
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@client.hybrid_command()
|
||||||
|
async def viewwarn(ctx, warning_id: int):
|
||||||
|
"""View detailed information about a specific warning (Requires Permission Level 5 or higher)"""
|
||||||
|
try:
|
||||||
|
# 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"]):
|
||||||
|
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
|
||||||
|
|
||||||
|
# Get warning details from database
|
||||||
|
connection = None
|
||||||
|
cursor = None
|
||||||
|
try:
|
||||||
|
connection = connect_to_database()
|
||||||
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
select_query = """
|
||||||
|
SELECT user_id, guild_id, moderator_id, reason, created_at, message_id,
|
||||||
|
message_content, message_attachments, message_author_id, message_channel_id
|
||||||
|
FROM user_warnings
|
||||||
|
WHERE id = %s AND guild_id = %s
|
||||||
|
"""
|
||||||
|
|
||||||
|
cursor.execute(select_query, (warning_id, ctx.guild.id))
|
||||||
|
result = cursor.fetchone()
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="❌ Warning Not Found",
|
||||||
|
description=f"No warning with ID {warning_id} found in this server.",
|
||||||
|
color=0xff0000
|
||||||
|
)
|
||||||
|
await ctx.send(embed=embed, ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse result
|
||||||
|
user_id, guild_id, moderator_id, reason, created_at, message_id, message_content, message_attachments, message_author_id, message_channel_id = result
|
||||||
|
|
||||||
|
# Get user and moderator objects
|
||||||
|
warned_user = await client.fetch_user(user_id)
|
||||||
|
moderator = await client.fetch_user(moderator_id)
|
||||||
|
|
||||||
|
# Create detailed embed
|
||||||
|
embed = discord.Embed(
|
||||||
|
title=f"⚠️ Warning Details - ID: {warning_id}",
|
||||||
|
color=0xff9500,
|
||||||
|
timestamp=created_at
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(name="👤 Warned User", value=f"{warned_user.mention}\n`{warned_user.id}`", inline=True)
|
||||||
|
embed.add_field(name="👮 Moderator", value=f"{moderator.mention}\n`{moderator.id}`", inline=True)
|
||||||
|
embed.add_field(name="📅 Date", value=f"<t:{int(created_at.timestamp())}:F>", inline=True)
|
||||||
|
embed.add_field(name="📝 Reason", value=reason, inline=False)
|
||||||
|
|
||||||
|
# Add message information if available
|
||||||
|
if message_id:
|
||||||
|
message_info = f"**Message ID:** `{message_id}`\n"
|
||||||
|
|
||||||
|
if message_channel_id:
|
||||||
|
message_info += f"**Channel:** <#{message_channel_id}>\n"
|
||||||
|
|
||||||
|
if message_author_id:
|
||||||
|
try:
|
||||||
|
msg_author = await client.fetch_user(message_author_id)
|
||||||
|
message_info += f"**Author:** {msg_author.mention}\n"
|
||||||
|
except:
|
||||||
|
message_info += f"**Author ID:** `{message_author_id}`\n"
|
||||||
|
|
||||||
|
if message_content:
|
||||||
|
content_display = message_content
|
||||||
|
if len(content_display) > 500:
|
||||||
|
content_display = content_display[:497] + "..."
|
||||||
|
message_info += f"**Content:**\n```\n{content_display}\n```"
|
||||||
|
|
||||||
|
embed.add_field(name="📄 Referenced Message", value=message_info, inline=False)
|
||||||
|
|
||||||
|
# Handle attachments
|
||||||
|
if message_attachments:
|
||||||
|
try:
|
||||||
|
attachments = json.loads(message_attachments)
|
||||||
|
if attachments:
|
||||||
|
attachment_info = ""
|
||||||
|
for i, att in enumerate(attachments[:3]): # Show max 3 attachments
|
||||||
|
attachment_info += f"**{att['filename']}** ({att['size']} bytes)\n"
|
||||||
|
|
||||||
|
# Show image if available and encoded
|
||||||
|
if att.get('data') and att['content_type'].startswith('image/'):
|
||||||
|
try:
|
||||||
|
import base64
|
||||||
|
import io
|
||||||
|
|
||||||
|
# Create temporary file-like object
|
||||||
|
image_data = base64.b64decode(att['data'])
|
||||||
|
file = discord.File(io.BytesIO(image_data), filename=att['filename'])
|
||||||
|
|
||||||
|
# Send image separately if it's the first attachment
|
||||||
|
if i == 0:
|
||||||
|
await ctx.send(f"📎 **Attachment from Warning {warning_id}:**", file=file)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not display attachment: {e}")
|
||||||
|
|
||||||
|
if len(attachments) > 3:
|
||||||
|
attachment_info += f"*... and {len(attachments) - 3} more attachment(s)*"
|
||||||
|
|
||||||
|
embed.add_field(name="📎 Attachments", value=attachment_info, inline=False)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error processing attachments: {e}")
|
||||||
|
|
||||||
|
embed.set_thumbnail(url=warned_user.display_avatar.url)
|
||||||
|
embed.set_footer(text=f"Warning ID: {warning_id} | Guild: {ctx.guild.name}")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
if cursor:
|
||||||
|
cursor.close()
|
||||||
|
if connection:
|
||||||
|
close_database_connection(connection)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in viewwarn command: {e}")
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="❌ Error",
|
||||||
|
description="An error occurred while retrieving warning details. Please try again.",
|
||||||
|
color=0xff0000
|
||||||
|
)
|
||||||
|
await ctx.send(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"):
|
||||||
"""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)
|
||||||
|
|||||||
Reference in New Issue
Block a user