From 4f70efb69f50775570de6be421a4237498f2bb5f Mon Sep 17 00:00:00 2001 From: Lilac-Rose Date: Sun, 4 Jan 2026 21:15:10 +0100 Subject: [PATCH] made botban not kill anyone --- events/botban.py | 79 +++++++++++++++++++++++++++++++++++++----------- xp/sync.py | 36 +++++----------------- 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/events/botban.py b/events/botban.py index 481d801..4432c9f 100644 --- a/events/botban.py +++ b/events/botban.py @@ -1,9 +1,12 @@ import discord from discord.ext import commands +from datetime import datetime, timedelta, timezone +import sys ROLE_ID_TO_BAN = 1439354601672282335 LOG_CHANNEL_ID = 1440055015711703242 - +FALLBACK_CHANNEL_ID = 876772600704020533 +NEW_MEMBER_THRESHOLD_DAYS = 1 class AutoBanOnRole(commands.Cog): def __init__(self, bot: commands.Bot): @@ -14,44 +17,86 @@ class AutoBanOnRole(commands.Cog): # Role was just added before_roles = set(before.roles) after_roles = set(after.roles) - added_roles = after_roles - before_roles - # Check if the trap role was added - if any(role.id == ROLE_ID_TO_BAN for role in added_roles): - guild = after.guild + trap_role = None + for role in added_roles: + if role.id == ROLE_ID_TO_BAN: + trap_role = role + break + + if trap_role is None: + return + + guild = after.guild - # Ban the user - try: + # Check how long they've been in the server + server_join_age = datetime.now(timezone.utc) - after.joined_at + is_new_member = server_join_age < timedelta(days=NEW_MEMBER_THRESHOLD_DAYS) + + if is_new_member: + try: await guild.ban( after, - reason="Bot automatically banned due to receiving trap role.", + reason="Bot automatically banned due to receiving bot trap role", delete_message_days=7 ) except Exception as e: - print(f"Failed to ban {after}: {e}") return - - # Log the event + channel = guild.get_channel(LOG_CHANNEL_ID) if channel is not None: embed = discord.Embed( title="🚫 Bot Detected & Auto-Banned", description=( - f"**User:** {after.mention} (`{after.id}`)\n" + f"**User:** {after.mention} `{after.id}`\n" f"**Action:** Automatically banned\n" - f"**Reason:** Received bot-trap role" + f"**Reason:** Received bot-trap role (server member for: {server_join_age.days} days)" ), color=discord.Color.red() ) embed.set_thumbnail(url=after.display_avatar.url) - await channel.send(embed=embed) + else: + warning_message = ( + f"**WARNING!** You were given a role that is designed to auto-ban bots. " + f"Since you're an established member, the role has been removed instead. " + f"Please be more careful next time." + ) + + dm_sent = False + try: + await after.send(warning_message) + dm_sent = True + except (discord.Forbidden, discord.HTTPException) as e: + pass + + await after.remove_roles(trap_role, reason="Auto removed trap role from established member") + + if not dm_sent: + fallback_channel = guild.get_channel(FALLBACK_CHANNEL_ID) + if fallback_channel is not None: + await fallback_channel.send(f"{after.mention}\n{warning_message}") + + log_channel = guild.get_channel(LOG_CHANNEL_ID) + if log_channel is not None: + embed = discord.Embed( + title="⚠️ Trap Role Given to Existing User", + description=( + f"**User:** {after.mention} `{after.id}`\n" + f"**Action:** Role removed, user warned\n" + f"**DM Sent:** {'Yes' if dm_sent else 'No (pinged in channel)'}\n" + f"**Server Member For:** {server_join_age.days} days" + ), + color=discord.Color.orange() + ) + embed.set_thumbnail(url=after.display_avatar.url) + await log_channel.send(embed=embed) @commands.Cog.listener() async def on_ready(self): - print("AutoBanOnRole cog loaded.") - + sys.stdout.write("[INFO] AutoBanOnRole cog loaded and ready!\n") + sys.stdout.flush() async def setup(bot: commands.Bot): - await bot.add_cog(AutoBanOnRole(bot)) + await bot.add_cog(AutoBanOnRole(bot)) \ No newline at end of file diff --git a/xp/sync.py b/xp/sync.py index 0e3d79d..c91a4e8 100644 --- a/xp/sync.py +++ b/xp/sync.py @@ -1,21 +1,12 @@ import discord from discord.ext import commands from discord import app_commands -from moderation.loader import ModerationBase +from moderation.loader import ModerationBase, ADMIN_ROLE_IDS, lilac_id from .database import get_db from .utils import load_config, xp_for_level from discord.utils import get import traceback import asyncio -import os - -# Load admin role IDs (same as in moderation base) -ADMIN_ROLE_IDS = { - int(role_id.strip()) - for role_id in os.getenv("ADMIN_ROLE_IDS", "").split(",") - if role_id.strip().isdigit() -} -LILAC_ID = 252130669919076352 class XPSync(commands.Cog): """Sync XP role rewards for users.""" @@ -23,14 +14,11 @@ class XPSync(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot - def check_admin_permission(self, user: discord.Member) -> bool: + def check_is_admin(self, user: discord.Member) -> bool: """Check if user has admin permissions.""" - # Check if user is Lilac - if user.id == LILAC_ID: - return True - - # Check if user has admin role - return any(role.id in ADMIN_ROLE_IDS for role in user.roles) + is_lilac = user.id == lilac_id + has_admin_role = any(role.id in ADMIN_ROLE_IDS for role in user.roles) + return has_admin_role or is_lilac async def sync_roles_for_user(self, member: discord.Member) -> tuple[int, list[str]]: """Sync roles for a member based on their lifetime XP level.""" @@ -86,18 +74,10 @@ class XPSync(commands.Cog): # If syncing someone else, check admin permission BEFORE deferring if user and user.id != interaction.user.id: - # Get the member object for permission checking - if not isinstance(interaction.user, discord.Member): + member = interaction.user + if not self.check_is_admin(member): await interaction.response.send_message( - "❌ Cannot verify permissions in DMs.", - ephemeral=True - ) - return - - # Check admin permissions - if not self.check_admin_permission(interaction.user): - await interaction.response.send_message( - "❌ You do not have permission to sync roles for other users.", + "You do not have permission to sync roles for other users.", ephemeral=True ) return