modified: bot.py
This commit is contained in:
423
bot.py
423
bot.py
@@ -2619,7 +2619,7 @@ async def log_moderation_action(guild, action_type, moderator, target_user, reas
|
||||
logger.error(f"Error logging moderation action: {e}")
|
||||
|
||||
async def save_warning_to_database(user_id, guild_id, moderator_id, reason, timestamp=None, message_data=None, message_id=None):
|
||||
"""Saves individual warning records to the database with optional message data"""
|
||||
"""Saves individual warning records to the database with optional message data and context"""
|
||||
connection = None
|
||||
cursor = None
|
||||
try:
|
||||
@@ -2635,29 +2635,44 @@ async def save_warning_to_database(user_id, guild_id, moderator_id, reason, time
|
||||
message_attachments = None
|
||||
message_author_id = None
|
||||
message_channel_id = None
|
||||
context_messages = None
|
||||
|
||||
if message_data:
|
||||
message_id_db = 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')
|
||||
if isinstance(message_data, dict) and "main_message" in message_data:
|
||||
# New format with context messages
|
||||
main_msg = message_data.get("main_message")
|
||||
if main_msg:
|
||||
message_id_db = main_msg.get('id')
|
||||
message_content = main_msg.get('content')
|
||||
message_attachments = main_msg.get('attachments')
|
||||
message_author_id = main_msg.get('author_id')
|
||||
message_channel_id = main_msg.get('channel_id')
|
||||
|
||||
# Store all context messages as JSON
|
||||
context_messages = json.dumps(message_data.get("context_messages", []))
|
||||
else:
|
||||
# Old format - single message
|
||||
message_id_db = 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 INTO user_warnings (user_id, guild_id, moderator_id, reason, message_id,
|
||||
message_content, message_attachments, message_author_id,
|
||||
message_channel_id, created_at)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
message_channel_id, context_messages, created_at)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
"""
|
||||
|
||||
cursor.execute(insert_query, (
|
||||
user_id, guild_id, moderator_id, reason, message_id_db,
|
||||
message_content, message_attachments, message_author_id,
|
||||
message_channel_id, timestamp
|
||||
message_channel_id, context_messages, timestamp
|
||||
))
|
||||
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} with context")
|
||||
return cursor.lastrowid
|
||||
|
||||
except Exception as e:
|
||||
@@ -2691,6 +2706,7 @@ def create_warnings_table():
|
||||
message_attachments LONGTEXT NULL,
|
||||
message_author_id BIGINT NULL,
|
||||
message_channel_id BIGINT NULL,
|
||||
context_messages LONGTEXT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_guild (user_id, guild_id),
|
||||
INDEX idx_created_at (created_at),
|
||||
@@ -2707,6 +2723,7 @@ def create_warnings_table():
|
||||
"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 COLUMN context_messages LONGTEXT NULL",
|
||||
"ALTER TABLE user_warnings ADD INDEX idx_message_id (message_id)"
|
||||
]
|
||||
|
||||
@@ -2738,7 +2755,7 @@ async def get_user_warnings(user_id, guild_id):
|
||||
|
||||
select_query = """
|
||||
SELECT id, moderator_id, reason, created_at, message_id, message_content,
|
||||
message_attachments, message_author_id, message_channel_id
|
||||
message_attachments, message_author_id, message_channel_id, context_messages
|
||||
FROM user_warnings
|
||||
WHERE user_id = %s AND guild_id = %s
|
||||
ORDER BY created_at DESC
|
||||
@@ -2758,7 +2775,8 @@ async def get_user_warnings(user_id, guild_id):
|
||||
"message_content": row[5],
|
||||
"message_attachments": row[6],
|
||||
"message_author_id": row[7],
|
||||
"message_channel_id": row[8]
|
||||
"message_channel_id": row[8],
|
||||
"context_messages": row[9]
|
||||
})
|
||||
|
||||
return warnings
|
||||
@@ -2772,61 +2790,93 @@ async def get_user_warnings(user_id, guild_id):
|
||||
if connection:
|
||||
close_database_connection(connection)
|
||||
|
||||
async def get_message_data(channel, message_id):
|
||||
"""Retrieves and processes message data for warning documentation"""
|
||||
async def get_message_data(channel, message_id, context_range=3):
|
||||
"""Retrieves and processes message data for warning documentation with context messages"""
|
||||
try:
|
||||
message = await channel.fetch_message(message_id)
|
||||
# Get the main message
|
||||
main_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
|
||||
# Get context messages (before and after)
|
||||
context_messages = []
|
||||
try:
|
||||
# Get messages around the target message
|
||||
async for msg in channel.history(limit=context_range * 2 + 1, around=main_message.created_at):
|
||||
context_messages.append(msg)
|
||||
|
||||
# Sort messages by timestamp
|
||||
context_messages.sort(key=lambda m: m.created_at)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not fetch context messages: {e}")
|
||||
context_messages = [main_message]
|
||||
|
||||
# Process all messages (main + context)
|
||||
all_messages_data = []
|
||||
|
||||
for message in context_messages:
|
||||
# Process attachments for this message
|
||||
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 for this message
|
||||
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)
|
||||
|
||||
# Create message data
|
||||
msg_data = {
|
||||
"id": message.id,
|
||||
"content": message.content,
|
||||
"author_id": message.author.id,
|
||||
"author_name": message.author.display_name,
|
||||
"author_username": message.author.name,
|
||||
"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,
|
||||
"is_main_message": message.id == message_id # Mark the main referenced message
|
||||
}
|
||||
|
||||
# Download and encode image attachments for permanent storage
|
||||
if attachment.content_type and attachment.content_type.startswith('image/'):
|
||||
try:
|
||||
import aiohttp
|
||||
import base64
|
||||
all_messages_data.append(msg_data)
|
||||
|
||||
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 structured data with main message and context
|
||||
return {
|
||||
"main_message": next((msg for msg in all_messages_data if msg["is_main_message"]), None),
|
||||
"context_messages": all_messages_data,
|
||||
"context_range": context_range,
|
||||
"total_messages": len(all_messages_data)
|
||||
}
|
||||
|
||||
return message_data
|
||||
@@ -2915,17 +2965,19 @@ async def restore_user_roles(user, guild):
|
||||
close_database_connection(connection)
|
||||
|
||||
@client.hybrid_command()
|
||||
async def warn(ctx, user: discord.User, reason: str = "No reason provided", message_id: str = None):
|
||||
async def warn(ctx, user: discord.User, reason: str = "No reason provided", message_id: str = None, context_range: int = 3):
|
||||
"""Warns a user (Requires Permission Level 5 or higher)
|
||||
|
||||
Usage:
|
||||
/warn @user "Inappropriate behavior"
|
||||
/warn @user "Bad language" 1407754702564884622
|
||||
/warn @user "Spam" 1407754702564884622 15
|
||||
|
||||
Parameters:
|
||||
- user: The user to warn
|
||||
- reason: Reason for the warning
|
||||
- message_id: Optional message ID to reference
|
||||
- context_range: Number of messages before/after to archive (default: 3, max: 25)
|
||||
"""
|
||||
# Check if it's a slash command and defer if needed
|
||||
is_slash_command = hasattr(ctx, 'interaction') and ctx.interaction
|
||||
@@ -2948,19 +3000,50 @@ async def warn(ctx, user: discord.User, reason: str = "No reason provided", mess
|
||||
pass
|
||||
|
||||
try:
|
||||
# Parse message ID and context range from reason if provided inline
|
||||
original_reason = reason
|
||||
|
||||
# Check if reason ends with potential message ID and/or context range
|
||||
reason_words = reason.split()
|
||||
parsed_message_id = message_id
|
||||
parsed_context_range = context_range
|
||||
|
||||
# Look for patterns like "reason 1234567890123456789" or "reason 1234567890123456789 15"
|
||||
if len(reason_words) >= 2:
|
||||
# Check if last word is a number (could be context range)
|
||||
if reason_words[-1].isdigit() and len(reason_words[-1]) <= 3:
|
||||
potential_context = int(reason_words[-1])
|
||||
if 1 <= potential_context <= 25: # Valid context range
|
||||
parsed_context_range = potential_context
|
||||
reason_words = reason_words[:-1] # Remove context range from reason
|
||||
|
||||
# Check if last word (after removing context) is a message ID
|
||||
if len(reason_words) >= 2 and len(reason_words[-1]) >= 17 and len(reason_words[-1]) <= 20 and reason_words[-1].isdigit():
|
||||
parsed_message_id = reason_words[-1]
|
||||
reason_words = reason_words[:-1] # Remove message ID from reason
|
||||
|
||||
# Update reason without the message ID and context range
|
||||
reason = " ".join(reason_words)
|
||||
|
||||
# Validate and limit context range
|
||||
if parsed_context_range < 1:
|
||||
parsed_context_range = 1
|
||||
elif parsed_context_range > 25:
|
||||
parsed_context_range = 25
|
||||
|
||||
# message_data will be populated if message_id is provided
|
||||
message_data = None
|
||||
|
||||
# Try to get message data if message ID was provided
|
||||
if message_id:
|
||||
if parsed_message_id:
|
||||
# Convert message_id string to int
|
||||
try:
|
||||
message_id_int = int(message_id)
|
||||
message_id_int = int(parsed_message_id)
|
||||
except ValueError:
|
||||
await send_response(content=f"❌ Invalid message ID: {message_id}")
|
||||
await send_response(content=f"❌ Invalid message ID: {parsed_message_id}")
|
||||
return
|
||||
# Try to get message data from current channel first
|
||||
message_data = await get_message_data(ctx.channel, message_id_int)
|
||||
# Try to get message data from current channel first with specified context range
|
||||
message_data = await get_message_data(ctx.channel, message_id_int, parsed_context_range)
|
||||
|
||||
# If not found in current channel, try other channels the bot can access
|
||||
if message_data is None:
|
||||
@@ -2970,7 +3053,7 @@ async def warn(ctx, user: discord.User, reason: str = "No reason provided", mess
|
||||
if channel.id == ctx.channel.id:
|
||||
continue # Skip current channel, already checked
|
||||
try:
|
||||
message_data = await get_message_data(channel, message_id_int)
|
||||
message_data = await get_message_data(channel, message_id_int, parsed_context_range)
|
||||
if message_data is not None:
|
||||
break
|
||||
except discord.Forbidden:
|
||||
@@ -3045,25 +3128,48 @@ async def warn(ctx, user: discord.User, reason: str = "No reason provided", mess
|
||||
|
||||
# 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"
|
||||
# Handle new context message format
|
||||
if isinstance(message_data, dict) and "main_message" in message_data:
|
||||
main_msg = message_data.get("main_message")
|
||||
context_msgs = message_data.get("context_messages", [])
|
||||
|
||||
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"
|
||||
if main_msg:
|
||||
message_info = f"**Message ID:** `{main_msg['id']}`\n"
|
||||
message_info += f"**Channel:** <#{main_msg['channel_id']}>\n"
|
||||
message_info += f"**Author:** {main_msg['author_name']}\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"
|
||||
if main_msg['content']:
|
||||
content_preview = main_msg['content']
|
||||
if len(content_preview) > 100:
|
||||
content_preview = content_preview[:97] + "..."
|
||||
message_info += f"**Content:** {content_preview}\n"
|
||||
|
||||
embed.add_field(name="📄 Referenced Message", value=message_info, inline=False)
|
||||
# Show attachment info
|
||||
if main_msg['attachments']:
|
||||
attachments = json.loads(main_msg['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"
|
||||
|
||||
# Add context info
|
||||
if len(context_msgs) > 1:
|
||||
message_info += f"\n**Context:** {len(context_msgs)} messages archived (±{parsed_context_range})"
|
||||
|
||||
embed.add_field(name="📄 Referenced Message", value=message_info, inline=False)
|
||||
else:
|
||||
# Handle old format for backward compatibility
|
||||
message_info = f"**Message ID:** `{message_data.get('id', 'Unknown')}`\n"
|
||||
message_info += f"**Channel:** <#{message_data.get('channel_id', 'Unknown')}>\n"
|
||||
|
||||
if message_data.get('content'):
|
||||
content_preview = message_data['content']
|
||||
if len(content_preview) > 100:
|
||||
content_preview = content_preview[:97] + "..."
|
||||
message_info += f"**Content:** {content_preview}\n"
|
||||
|
||||
embed.add_field(name="📄 Referenced Message", value=message_info, inline=False)
|
||||
elif message_id:
|
||||
embed.add_field(
|
||||
name="⚠️ Message Not Found",
|
||||
@@ -3544,7 +3650,7 @@ async def viewwarn(ctx, warning_id: int):
|
||||
|
||||
select_query = """
|
||||
SELECT user_id, guild_id, moderator_id, reason, created_at, message_id,
|
||||
message_content, message_attachments, message_author_id, message_channel_id
|
||||
message_content, message_attachments, message_author_id, message_channel_id, context_messages
|
||||
FROM user_warnings
|
||||
WHERE id = %s AND guild_id = %s
|
||||
"""
|
||||
@@ -3562,7 +3668,7 @@ async def viewwarn(ctx, warning_id: int):
|
||||
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
|
||||
user_id, guild_id, moderator_id, reason, created_at, message_id, message_content, message_attachments, message_author_id, message_channel_id, context_messages = result
|
||||
|
||||
# Get user and moderator objects
|
||||
warned_user = await client.fetch_user(user_id)
|
||||
@@ -3637,6 +3743,43 @@ async def viewwarn(ctx, warning_id: int):
|
||||
embed.set_thumbnail(url=warned_user.display_avatar.url)
|
||||
embed.set_footer(text=f"Warning ID: {warning_id} | Guild: {ctx.guild.name}")
|
||||
|
||||
# Display context messages if available
|
||||
if context_messages:
|
||||
try:
|
||||
context_data = json.loads(context_messages)
|
||||
if context_data and len(context_data) > 1:
|
||||
context_display = "**📋 Message Context:**\n"
|
||||
|
||||
for i, msg in enumerate(context_data):
|
||||
timestamp = datetime.fromisoformat(msg['created_at'].replace('Z', '+00:00'))
|
||||
author_name = msg.get('author_name', 'Unknown')
|
||||
content = msg.get('content', '*No content*')
|
||||
|
||||
# Truncate long messages
|
||||
if len(content) > 100:
|
||||
content = content[:97] + "..."
|
||||
|
||||
# Mark the main message
|
||||
marker = "🎯 " if msg.get('is_main_message') else "💬 "
|
||||
|
||||
context_display += f"{marker}**{author_name}** (<t:{int(timestamp.timestamp())}:t>):\n`{content}`\n\n"
|
||||
|
||||
# Limit to prevent embed overflow
|
||||
if len(context_display) > 1800:
|
||||
context_display += "*... (truncated)*"
|
||||
break
|
||||
|
||||
# Send context as separate message to avoid embed limits
|
||||
context_embed = discord.Embed(
|
||||
title=f"📋 Message Context for Warning {warning_id}",
|
||||
description=context_display,
|
||||
color=0x3498db
|
||||
)
|
||||
await send_response(embed=context_embed)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error displaying context messages: {e}")
|
||||
|
||||
await send_response(embed=embed)
|
||||
|
||||
finally:
|
||||
@@ -3655,23 +3798,28 @@ 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", message_id: str = None):
|
||||
async def mute(ctx, user: discord.User, duration: str, reason: str = "No reason provided", message_id: str = None, context_range: int = 3):
|
||||
"""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
|
||||
/mute @user 1h "Bad language" 1407754702564884622 15
|
||||
|
||||
Parameters:
|
||||
- user: The user to mute
|
||||
- duration: Duration (10m, 1h, 2d)
|
||||
- reason: Reason for the mute
|
||||
- reason: Reason for the mute (can include message ID and context range)
|
||||
- message_id: Optional message ID to reference
|
||||
- context_range: Number of context messages to archive (1-25, default: 3)
|
||||
|
||||
Duration examples:
|
||||
- 10m = 10 minutes
|
||||
- 1h = 1 hour
|
||||
- 2d = 2 days
|
||||
|
||||
You can also specify message ID and context range in the reason:
|
||||
"Bad language 1407754702564884622 15" (15 messages before/after)
|
||||
"""
|
||||
# Check if it's a slash command and defer if needed
|
||||
is_slash_command = hasattr(ctx, 'interaction') and ctx.interaction
|
||||
@@ -3694,16 +3842,36 @@ async def mute(ctx, user: discord.User, duration: str, reason: str = "No reason
|
||||
pass
|
||||
|
||||
try:
|
||||
# Parse message ID from reason if it looks like a message ID
|
||||
# Parse message ID and context range from reason if they look valid
|
||||
original_reason = reason
|
||||
message_data = None
|
||||
parsed_context_range = context_range
|
||||
|
||||
# Check if reason ends with a potential message ID
|
||||
# Check if reason contains potential message ID and context range
|
||||
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
|
||||
if len(reason_words) >= 2:
|
||||
# Check for pattern: "reason text 1234567890123456789 15"
|
||||
potential_msg_id = reason_words[-2] if len(reason_words) >= 2 else None
|
||||
potential_context = reason_words[-1] if len(reason_words) >= 1 else None
|
||||
|
||||
# Check if last two elements are message ID and context range
|
||||
if (potential_msg_id and len(potential_msg_id) >= 17 and len(potential_msg_id) <= 20 and potential_msg_id.isdigit() and
|
||||
potential_context and len(potential_context) <= 3 and potential_context.isdigit()):
|
||||
parsed_context_range = int(potential_context)
|
||||
message_id = potential_msg_id
|
||||
reason = " ".join(reason_words[:-2]) # Remove both from reason
|
||||
elif len(reason_words) >= 1:
|
||||
# Check if reason ends with a potential message ID only
|
||||
potential_msg_id = reason_words[-1]
|
||||
if len(potential_msg_id) >= 17 and len(potential_msg_id) <= 20 and potential_msg_id.isdigit():
|
||||
message_id = potential_msg_id
|
||||
reason = " ".join(reason_words[:-1]) # Remove message ID from reason
|
||||
|
||||
# Validate and limit context range
|
||||
if parsed_context_range < 1:
|
||||
parsed_context_range = 1
|
||||
elif parsed_context_range > 25:
|
||||
parsed_context_range = 25
|
||||
|
||||
# Try to get message data if message ID was provided
|
||||
if message_id:
|
||||
@@ -3714,7 +3882,7 @@ async def mute(ctx, user: discord.User, duration: str, reason: str = "No reason
|
||||
return
|
||||
|
||||
# Try to get message data from current channel first
|
||||
message_data = await get_message_data(ctx.channel, message_id_int)
|
||||
message_data = await get_message_data(ctx.channel, message_id_int, context_range=parsed_context_range)
|
||||
|
||||
# If not found in current channel, try other channels
|
||||
if message_data is None:
|
||||
@@ -3722,7 +3890,7 @@ async def mute(ctx, user: discord.User, duration: str, reason: str = "No reason
|
||||
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)
|
||||
message_data = await get_message_data(channel, message_id_int, context_range=parsed_context_range)
|
||||
if message_data is not None:
|
||||
break
|
||||
except discord.Forbidden:
|
||||
@@ -3841,27 +4009,48 @@ async def mute(ctx, user: discord.User, duration: str, reason: str = "No reason
|
||||
|
||||
# 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)
|
||||
# Handle new context message format
|
||||
if isinstance(message_data, dict) and "main_message" in message_data:
|
||||
main_msg = message_data.get("main_message")
|
||||
context_msgs = message_data.get("context_messages", [])
|
||||
|
||||
# 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
|
||||
if main_msg:
|
||||
message_info = f"**Message ID:** `{main_msg['id']}`\n"
|
||||
message_info += f"**Channel:** <#{main_msg['channel_id']}>\n"
|
||||
message_info += f"**Author:** {main_msg['author_name']}\n"
|
||||
|
||||
if main_msg['content']:
|
||||
content_preview = main_msg['content'][:200] + "..." if len(main_msg['content']) > 200 else main_msg['content']
|
||||
message_info += f"**Content:** {content_preview}"
|
||||
|
||||
# Add context info
|
||||
if len(context_msgs) > 1:
|
||||
message_info += f"\n**Context:** {len(context_msgs)} messages archived (±{parsed_context_range})"
|
||||
|
||||
embed.add_field(name="📄 Referenced Message", value=message_info, inline=False)
|
||||
|
||||
# Process attachments for archival if any
|
||||
if main_msg.get('attachments'):
|
||||
try:
|
||||
attachments_data = json.loads(main_msg['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
|
||||
else:
|
||||
# Handle old format for backward compatibility
|
||||
message_info = f"**Message ID:** `{message_data.get('id', 'Unknown')}`\n"
|
||||
message_info += f"**Channel:** <#{message_data.get('channel_id', 'Unknown')}>\n"
|
||||
message_info += f"**Author:** <@{message_data.get('author_id', 'Unknown')}>\n"
|
||||
if message_data.get('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)
|
||||
elif message_id:
|
||||
embed.add_field(name="📄 Referenced Message", value=f"Message ID: `{message_id}` (Message not found or inaccessible)", inline=False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user