modified: bot.py

This commit is contained in:
SimolZimol
2026-06-15 22:41:28 +02:00
parent 7edf0e34a6
commit 7b8c82869c

218
bot.py
View File

@@ -2894,12 +2894,33 @@ async def on_message(message):
member_role_ids = {role.id for role in member.roles} member_role_ids = {role.id for role in member.roles}
if not (ignore_role_ids & member_role_ids): if not (ignore_role_ids & member_role_ids):
archived_message_data = {
"id": message.id,
"channel_id": message.channel.id,
"author_id": member.id,
"author_name": str(member),
"content": message.content,
"attachments": json.dumps(
[
{
"filename": a.filename,
"url": a.url,
"content_type": a.content_type,
"size": a.size
}
for a in message.attachments
]
) if message.attachments else "[]"
}
try: try:
await message.delete() await message.delete()
except Exception: except Exception:
pass pass
action_taken = None action_taken = None
honeypot_mute_id = None
acc_age_min = int(guild_settings.get("honeypot_acc_age_min") or 30) acc_age_min = int(guild_settings.get("honeypot_acc_age_min") or 30)
preserve = guild_settings.get("honeypot_preserve_old_accounts", False) preserve = guild_settings.get("honeypot_preserve_old_accounts", False)
@@ -2913,7 +2934,7 @@ async def on_message(message):
if is_old_account: if is_old_account:
try: try:
result = await apply_mute_action( result = await apply_full_mute(
guild=message.guild, guild=message.guild,
member=member, member=member,
moderator=client.user, moderator=client.user,
@@ -2921,14 +2942,16 @@ async def on_message(message):
duration_label="365d", duration_label="365d",
reason="Honeypot: wrote in honeypot channel (protected old member)", reason="Honeypot: wrote in honeypot channel (protected old member)",
source_channel=message.channel, source_channel=message.channel,
message_data=None, message_data=archived_message_data,
message_id=message.id, message_id=message.id,
remove_existing_roles=False, send_dm=True,
save_removed_roles=False, log_action=True,
remove_existing_roles=True,
save_current_roles=True,
increment_mute_count=True increment_mute_count=True
) )
action_taken = "mute"
honeypot_mute_id = result["mute_id"] honeypot_mute_id = result["mute_id"]
action_taken = "mute"
except discord.Forbidden: except discord.Forbidden:
logger.warning(f"Honeypot: no permission to mute {member.id} in guild {guild_id}") logger.warning(f"Honeypot: no permission to mute {member.id} in guild {guild_id}")
except Exception as e: except Exception as e:
@@ -2964,7 +2987,7 @@ async def on_message(message):
embed.add_field(name="User", value=f"{member} (`{member.id}`)", inline=True) embed.add_field(name="User", value=f"{member} (`{member.id}`)", inline=True)
embed.add_field(name="Channel", value=f"<#{message.channel.id}>", inline=True) embed.add_field(name="Channel", value=f"<#{message.channel.id}>", inline=True)
if action_taken == "mute": if honeypot_mute_id:
embed.add_field(name="Mute Record ID", value=f"`{honeypot_mute_id}`", inline=True) embed.add_field(name="Mute Record ID", value=f"`{honeypot_mute_id}`", inline=True)
if preserve: if preserve:
@@ -6484,7 +6507,7 @@ async def mute(
context_range: int = 3, context_range: int = 3,
silent: bool = False silent: bool = False
): ):
"""Mute a user for a specified duration.""" """Mutes a user for a specified duration (Requires Permission Level 5 or higher)."""
is_slash_command = hasattr(ctx, "interaction") and ctx.interaction is not None is_slash_command = hasattr(ctx, "interaction") and ctx.interaction is not None
@@ -6503,17 +6526,16 @@ async def mute(
else: else:
await ctx.send(content=content, embed=embed, file=file) await ctx.send(content=content, embed=embed, file=file)
except Exception as e: except Exception as e:
logger.error(f"Error sending mute response: {e}") logger.error(f"Error sending response in mute command: {e}")
try: try:
if embed: if embed:
await ctx.send(embed=embed) await ctx.send(embed=embed)
elif content: elif content:
await ctx.send(content) await ctx.send(content=content)
except Exception as fallback_error: except Exception as fallback_error:
logger.error(f"Fallback send failed: {fallback_error}") logger.error(f"Fallback send also failed: {fallback_error}")
try: try:
original_reason = reason
message_data = None message_data = None
parsed_context_range = 3 parsed_context_range = 3
@@ -6523,16 +6545,13 @@ async def mute(
potential_context = reason_words[-1] potential_context = reason_words[-1]
if ( if (
potential_msg_id potential_msg_id and 17 <= len(potential_msg_id) <= 20 and potential_msg_id.isdigit()
and 17 <= len(potential_msg_id) <= 20 and potential_context and len(potential_context) <= 3 and potential_context.isdigit()
and potential_msg_id.isdigit()
and potential_context
and len(potential_context) <= 3
and potential_context.isdigit()
): ):
parsed_context_range = int(potential_context) parsed_context_range = int(potential_context)
message_id = potential_msg_id message_id = potential_msg_id
reason = " ".join(reason_words[:-2]) reason = " ".join(reason_words[:-2])
elif len(reason_words) >= 1: elif len(reason_words) >= 1:
potential_msg_id = reason_words[-1] potential_msg_id = reason_words[-1]
if 17 <= len(potential_msg_id) <= 20 and potential_msg_id.isdigit(): if 17 <= len(potential_msg_id) <= 20 and potential_msg_id.isdigit():
@@ -6610,7 +6629,7 @@ async def mute(
await send_response(embed=embed, ephemeral=True) await send_response(embed=embed, ephemeral=True)
return return
result = await apply_mute_action( result = await apply_full_mute(
guild=ctx.guild, guild=ctx.guild,
member=member, member=member,
moderator=ctx.author, moderator=ctx.author,
@@ -6620,15 +6639,16 @@ async def mute(
source_channel=ctx.channel, source_channel=ctx.channel,
message_data=message_data, message_data=message_data,
message_id=int(message_id) if message_id else None, message_id=int(message_id) if message_id else None,
send_dm=True,
log_action=True,
remove_existing_roles=True, remove_existing_roles=True,
save_removed_roles=True, save_current_roles=True,
increment_mute_count=True increment_mute_count=True
) )
end_time = result["end_time"] end_time = result["end_time"]
mute_id = result["mute_id"] mute_id = result["mute_id"]
process_uuid = result["process_uuid"] process_uuid = result["process_uuid"]
mute_role = result["mute_role"]
user_data = result["user_data"] user_data = result["user_data"]
embed = discord.Embed( embed = discord.Embed(
@@ -6641,18 +6661,22 @@ async def mute(
embed.add_field(name="⏰ Ends At", value=f"<t:{int(end_time.timestamp())}:F>", 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 or "No reason provided", inline=False) embed.add_field(name="📝 Reason", value=reason or "No reason provided", 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=str(user_data["mutes"]), inline=True) embed.add_field(name="🔇 Mute Count", value=f"{user_data['mutes']}", inline=True)
if message_data: if message_data:
if isinstance(message_data, dict) and "main_message" in message_data: if isinstance(message_data, dict) and "main_message" in message_data:
main_msg = message_data.get("main_message") main_msg = message_data.get("main_message")
context_msgs = message_data.get("context_messages", [])
if main_msg: if main_msg:
message_info = f"**Message ID:** `{main_msg['id']}`\n" message_info = f"**Message ID:** `{main_msg['id']}`\n"
message_info += f"**Channel:** <#{main_msg['channel_id']}>\n" message_info += f"**Channel:** <#{main_msg['channel_id']}>\n"
message_info += f"**Author:** {main_msg['author_name']}\n" message_info += f"**Author:** {main_msg['author_name']}\n"
if main_msg.get("content"): if main_msg.get("content"):
content_preview = main_msg["content"][:200] + "..." if len(main_msg["content"]) > 200 else 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}" message_info += f"**Content:** {content_preview}"
embed.add_field(name="📄 Referenced Message", value=message_info, inline=False) embed.add_field(name="📄 Referenced Message", value=message_info, inline=False)
if main_msg.get("attachments"): if main_msg.get("attachments"):
@@ -6660,7 +6684,7 @@ async def mute(
attachments_data = json.loads(main_msg["attachments"]) attachments_data = json.loads(main_msg["attachments"])
if attachments_data: if attachments_data:
attachment_info = "" attachment_info = ""
for i, att in enumerate(attachments_data[:3]): for att in attachments_data[:3]:
attachment_info += f"{att.get('filename', 'Unknown file')}\n" attachment_info += f"{att.get('filename', 'Unknown file')}\n"
if len(attachments_data) > 3: if len(attachments_data) > 3:
attachment_info += f"• +{len(attachments_data) - 3} more attachments" attachment_info += f"• +{len(attachments_data) - 3} more attachments"
@@ -6696,85 +6720,21 @@ async def mute(
silent_embed.add_field(name="⏱️ Duration", value=duration, inline=True) silent_embed.add_field(name="⏱️ Duration", value=duration, inline=True)
silent_embed.add_field(name="⏰ Ends At", value=f"<t:{int(end_time.timestamp())}:F>", inline=True) silent_embed.add_field(name="⏰ Ends At", value=f"<t:{int(end_time.timestamp())}:F>", inline=True)
silent_embed.add_field(name="📝 Reason", value=reason or "No reason provided", inline=False) silent_embed.add_field(name="📝 Reason", value=reason or "No reason provided", inline=False)
silent_embed.add_field(name="🔇 Mute Count", value=str(user_data["mutes"]), inline=True) silent_embed.add_field(name="🔇 Mute Count", value=f"{user_data['mutes']}", inline=True)
silent_embed.add_field(name="🆔 Mute Record ID", value=f"`{mute_id}`", inline=True) silent_embed.add_field(name="🆔 Mute Record ID", value=f"`{mute_id}`", inline=True)
silent_embed.add_field( silent_embed.add_field(
name="🔔 Actions Taken", name="🔔 Actions Taken",
value="• User muted\n• User received DM notification if possible\n• Mod log entry created\n• No public announcement", value="• User muted\n Roles saved\n User received DM notification if possible\n• Mod log entry created\n• No public announcement",
inline=False inline=False
) )
silent_embed.set_footer(text=f"Silent Mode • User ID: {user.id} | Use /viewmute {mute_id} for details") silent_embed.set_footer(text=f"Silent Mode • User ID: {user.id} | Use /viewmute {mute_id} for details")
silent_embed.set_thumbnail(url=user.display_avatar.url) silent_embed.set_thumbnail(url=user.display_avatar.url)
try: await send_response(embed=silent_embed, ephemeral=True)
if is_slash_command: return
if hasattr(ctx, "followup") and ctx.followup is not None:
await ctx.followup.send(embed=silent_embed, ephemeral=True)
elif hasattr(ctx, "interaction") and ctx.interaction:
await ctx.interaction.followup.send(embed=silent_embed, ephemeral=True)
else:
raise RuntimeError("No followup available after defer")
else:
await ctx.send(embed=silent_embed)
except Exception as e:
logger.error(f"Error sending silent mute response: {e}")
else:
await send_response(embed=embed) await send_response(embed=embed)
log_additional_info = {
"Mute Count": str(user_data["mutes"]),
"Process ID": str(process_uuid)[:8],
"Mute Record ID": str(mute_id)
}
if message_data:
if isinstance(message_data, dict) and "main_message" in message_data:
main_msg = message_data.get("main_message")
if main_msg:
log_additional_info["Referenced Message"] = f"ID: {main_msg['id']} in <#{main_msg['channel_id']}>"
else:
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:
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 or "No reason provided", inline=False)
dm_embed.add_field(name="👮 Moderator", value=ctx.author.display_name, inline=True)
if message_data:
preview_content = None
if isinstance(message_data, dict) and "main_message" in message_data:
main_msg = message_data.get("main_message")
if main_msg:
preview_content = main_msg.get("content")
else:
preview_content = message_data.get("content")
if preview_content:
content_preview = preview_content[:200] + "..." if len(preview_content) > 200 else preview_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
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:
@@ -6786,8 +6746,7 @@ async def mute(
) )
await send_response(embed=embed, ephemeral=True) await send_response(embed=embed, ephemeral=True)
async def apply_full_mute(
async def apply_mute_action(
*, *,
guild: discord.Guild, guild: discord.Guild,
member: discord.Member, member: discord.Member,
@@ -6795,16 +6754,18 @@ async def apply_mute_action(
duration_seconds: int, duration_seconds: int,
duration_label: str, duration_label: str,
reason: str, reason: str,
source_channel=None, source_channel: discord.TextChannel | None = None,
message_data: dict | None = None, message_data: dict | None = None,
message_id: int | None = None, message_id: int | None = None,
send_dm: bool = True,
log_action: bool = True,
remove_existing_roles: bool = True, remove_existing_roles: bool = True,
save_removed_roles: bool = True, save_current_roles: bool = True,
increment_mute_count: bool = True increment_mute_count: bool = True
): ):
guild_settings = get_guild_settings(guild.id) guild_settings = get_guild_settings(guild.id)
if save_removed_roles: if save_current_roles:
await save_user_roles(member.id, guild.id, member.roles) await save_user_roles(member.id, guild.id, member.roles)
if remove_existing_roles: if remove_existing_roles:
@@ -6823,7 +6784,8 @@ async def apply_mute_action(
user_data["mutes"] += 1 user_data["mutes"] += 1
update_user_data(member.id, guild.id, "mutes", user_data["mutes"]) update_user_data(member.id, guild.id, "mutes", user_data["mutes"])
end_time = datetime.now() + timedelta(seconds=duration_seconds) start_time = datetime.now()
end_time = start_time + timedelta(seconds=duration_seconds)
process_data = { process_data = {
"user_id": member.id, "user_id": member.id,
@@ -6850,7 +6812,7 @@ async def apply_mute_action(
moderator_id=moderator.id, moderator_id=moderator.id,
reason=reason, reason=reason,
duration=duration_label, duration=duration_label,
start_time=datetime.now(), start_time=start_time,
end_time=end_time, end_time=end_time,
process_uuid=process_uuid, process_uuid=process_uuid,
channel_id=source_channel.id if source_channel else None, channel_id=source_channel.id if source_channel else None,
@@ -6859,9 +6821,69 @@ async def apply_mute_action(
message_id=message_id message_id=message_id
) )
if log_action:
additional_info = {
"Mute Count": str(user_data["mutes"]),
"Process ID": str(process_uuid)[:8],
"Mute Record ID": str(mute_id)
}
if message_data:
if isinstance(message_data, dict) and "main_message" in message_data:
main_msg = message_data.get("main_message")
if main_msg:
additional_info["Referenced Message"] = f"ID: {main_msg['id']} in <#{main_msg['channel_id']}>"
else:
if message_data.get("id") and message_data.get("channel_id"):
additional_info["Referenced Message"] = f"ID: {message_data['id']} in <#{message_data['channel_id']}>"
await log_moderation_action(
guild=guild,
action_type="mute",
moderator=moderator,
target_user=member,
reason=reason,
duration=duration_label,
additional_info=additional_info
)
if send_dm:
try:
dm_embed = discord.Embed(
title="🔇 You have been muted",
description=f"You have been muted in **{guild.name}**",
color=0xff0000,
timestamp=datetime.now()
)
dm_embed.add_field(name="⏱️ Duration", value=duration_label, 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 or "No reason provided", inline=False)
dm_embed.add_field(name="👮 Moderator", value=getattr(moderator, "display_name", str(moderator)), inline=True)
preview_content = None
if message_data:
if isinstance(message_data, dict) and "main_message" in message_data:
main_msg = message_data.get("main_message")
if main_msg:
preview_content = main_msg.get("content")
else:
preview_content = message_data.get("content")
if preview_content:
content_preview = preview_content[:200] + "..." if len(preview_content) > 200 else preview_content
dm_embed.add_field(name="📄 Referenced Message", value=f"```{content_preview}```", inline=False)
dm_embed.set_footer(text=f"Server: {guild.name}")
await member.send(embed=dm_embed)
except discord.Forbidden:
pass
except Exception as e:
logger.error(f"Failed to DM muted user {member.id}: {e}")
return { return {
"mute_role": mute_role, "mute_role": mute_role,
"user_data": user_data, "user_data": user_data,
"start_time": start_time,
"end_time": end_time, "end_time": end_time,
"process_uuid": process_uuid, "process_uuid": process_uuid,
"mute_id": mute_id "mute_id": mute_id