modified: bot.py
This commit is contained in:
220
bot.py
220
bot.py
@@ -2894,12 +2894,33 @@ async def on_message(message):
|
||||
|
||||
member_role_ids = {role.id for role in member.roles}
|
||||
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:
|
||||
await message.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
action_taken = None
|
||||
honeypot_mute_id = None
|
||||
|
||||
acc_age_min = int(guild_settings.get("honeypot_acc_age_min") or 30)
|
||||
preserve = guild_settings.get("honeypot_preserve_old_accounts", False)
|
||||
|
||||
@@ -2913,7 +2934,7 @@ async def on_message(message):
|
||||
|
||||
if is_old_account:
|
||||
try:
|
||||
result = await apply_mute_action(
|
||||
result = await apply_full_mute(
|
||||
guild=message.guild,
|
||||
member=member,
|
||||
moderator=client.user,
|
||||
@@ -2921,14 +2942,16 @@ async def on_message(message):
|
||||
duration_label="365d",
|
||||
reason="Honeypot: wrote in honeypot channel (protected old member)",
|
||||
source_channel=message.channel,
|
||||
message_data=None,
|
||||
message_data=archived_message_data,
|
||||
message_id=message.id,
|
||||
remove_existing_roles=False,
|
||||
save_removed_roles=False,
|
||||
send_dm=True,
|
||||
log_action=True,
|
||||
remove_existing_roles=True,
|
||||
save_current_roles=True,
|
||||
increment_mute_count=True
|
||||
)
|
||||
action_taken = "mute"
|
||||
honeypot_mute_id = result["mute_id"]
|
||||
action_taken = "mute"
|
||||
except discord.Forbidden:
|
||||
logger.warning(f"Honeypot: no permission to mute {member.id} in guild {guild_id}")
|
||||
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="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)
|
||||
|
||||
if preserve:
|
||||
@@ -2987,7 +3010,7 @@ async def on_message(message):
|
||||
logger.error(f"Honeypot: error sending log: {e}")
|
||||
|
||||
return
|
||||
# ── End honeypot check ─────────────────────────────────────────────────────────
|
||||
# ── End honeypot check ─────────────────────────────────────────────────────────
|
||||
cooldown_key = (user_id, guild_id)
|
||||
current_time = time.time()
|
||||
|
||||
@@ -6484,7 +6507,7 @@ async def mute(
|
||||
context_range: int = 3,
|
||||
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
|
||||
|
||||
@@ -6503,17 +6526,16 @@ async def mute(
|
||||
else:
|
||||
await ctx.send(content=content, embed=embed, file=file)
|
||||
except Exception as e:
|
||||
logger.error(f"Error sending mute response: {e}")
|
||||
logger.error(f"Error sending response in mute command: {e}")
|
||||
try:
|
||||
if embed:
|
||||
await ctx.send(embed=embed)
|
||||
elif content:
|
||||
await ctx.send(content)
|
||||
await ctx.send(content=content)
|
||||
except Exception as fallback_error:
|
||||
logger.error(f"Fallback send failed: {fallback_error}")
|
||||
logger.error(f"Fallback send also failed: {fallback_error}")
|
||||
|
||||
try:
|
||||
original_reason = reason
|
||||
message_data = None
|
||||
parsed_context_range = 3
|
||||
|
||||
@@ -6523,16 +6545,13 @@ async def mute(
|
||||
potential_context = reason_words[-1]
|
||||
|
||||
if (
|
||||
potential_msg_id
|
||||
and 17 <= len(potential_msg_id) <= 20
|
||||
and potential_msg_id.isdigit()
|
||||
and potential_context
|
||||
and len(potential_context) <= 3
|
||||
and potential_context.isdigit()
|
||||
potential_msg_id and 17 <= 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])
|
||||
|
||||
elif len(reason_words) >= 1:
|
||||
potential_msg_id = reason_words[-1]
|
||||
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)
|
||||
return
|
||||
|
||||
result = await apply_mute_action(
|
||||
result = await apply_full_mute(
|
||||
guild=ctx.guild,
|
||||
member=member,
|
||||
moderator=ctx.author,
|
||||
@@ -6620,15 +6639,16 @@ async def mute(
|
||||
source_channel=ctx.channel,
|
||||
message_data=message_data,
|
||||
message_id=int(message_id) if message_id else None,
|
||||
send_dm=True,
|
||||
log_action=True,
|
||||
remove_existing_roles=True,
|
||||
save_removed_roles=True,
|
||||
save_current_roles=True,
|
||||
increment_mute_count=True
|
||||
)
|
||||
|
||||
end_time = result["end_time"]
|
||||
mute_id = result["mute_id"]
|
||||
process_uuid = result["process_uuid"]
|
||||
mute_role = result["mute_role"]
|
||||
user_data = result["user_data"]
|
||||
|
||||
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="📝 Reason", value=reason or "No reason provided", inline=False)
|
||||
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 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 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.get("content"):
|
||||
content_preview = main_msg["content"][:200] + "..." if len(main_msg["content"]) > 200 else main_msg["content"]
|
||||
message_info += f"**Content:** {content_preview}"
|
||||
|
||||
embed.add_field(name="📄 Referenced Message", value=message_info, inline=False)
|
||||
|
||||
if main_msg.get("attachments"):
|
||||
@@ -6660,7 +6684,7 @@ async def mute(
|
||||
attachments_data = json.loads(main_msg["attachments"])
|
||||
if attachments_data:
|
||||
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"
|
||||
if len(attachments_data) > 3:
|
||||
attachment_info += f"• +{len(attachments_data) - 3} more attachments"
|
||||
@@ -6696,84 +6720,20 @@ async def mute(
|
||||
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="📝 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="🔔 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
|
||||
)
|
||||
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)
|
||||
|
||||
try:
|
||||
if is_slash_command:
|
||||
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=silent_embed, ephemeral=True)
|
||||
return
|
||||
|
||||
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
|
||||
await send_response(embed=embed)
|
||||
|
||||
logger.info(f"User {user.id} muted by {ctx.author.id} in guild {ctx.guild.id} for {duration}. Reason: {reason}")
|
||||
|
||||
@@ -6786,8 +6746,7 @@ async def mute(
|
||||
)
|
||||
await send_response(embed=embed, ephemeral=True)
|
||||
|
||||
|
||||
async def apply_mute_action(
|
||||
async def apply_full_mute(
|
||||
*,
|
||||
guild: discord.Guild,
|
||||
member: discord.Member,
|
||||
@@ -6795,16 +6754,18 @@ async def apply_mute_action(
|
||||
duration_seconds: int,
|
||||
duration_label: str,
|
||||
reason: str,
|
||||
source_channel=None,
|
||||
source_channel: discord.TextChannel | None = None,
|
||||
message_data: dict | None = None,
|
||||
message_id: int | None = None,
|
||||
send_dm: bool = True,
|
||||
log_action: bool = True,
|
||||
remove_existing_roles: bool = True,
|
||||
save_removed_roles: bool = True,
|
||||
save_current_roles: bool = True,
|
||||
increment_mute_count: bool = True
|
||||
):
|
||||
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)
|
||||
|
||||
if remove_existing_roles:
|
||||
@@ -6823,7 +6784,8 @@ async def apply_mute_action(
|
||||
user_data["mutes"] += 1
|
||||
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 = {
|
||||
"user_id": member.id,
|
||||
@@ -6850,7 +6812,7 @@ async def apply_mute_action(
|
||||
moderator_id=moderator.id,
|
||||
reason=reason,
|
||||
duration=duration_label,
|
||||
start_time=datetime.now(),
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
process_uuid=process_uuid,
|
||||
channel_id=source_channel.id if source_channel else None,
|
||||
@@ -6859,9 +6821,69 @@ async def apply_mute_action(
|
||||
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 {
|
||||
"mute_role": mute_role,
|
||||
"user_data": user_data,
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
"process_uuid": process_uuid,
|
||||
"mute_id": mute_id
|
||||
|
||||
Reference in New Issue
Block a user