Kuby/commandes/whitelist_monitor.py

297 lines
12 KiB
Python
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import disnake
from disnake.ext import commands
from disnake.ui import View, Button
import json
import os
from datetime import datetime
from typing import List, Dict, Optional
class WhitelistMonitor(commands.Cog):
"""Monitors whitelist members for role changes and server leave events."""
def __init__(self, bot):
self.bot = bot
self.whitelist_file = "data/whitelist.json"
self.persistent_views_added = False
self.big_permissions = [
disnake.Permissions.administrator,
disnake.Permissions.manage_guild,
disnake.Permissions.manage_roles,
disnake.Permissions.manage_channels,
disnake.Permissions.kick_members,
disnake.Permissions.ban_members,
disnake.Permissions.manage_messages,
disnake.Permissions.moderate_members,
disnake.Permissions.manage_nicknames,
disnake.Permissions.manage_emojis,
disnake.Permissions.manage_webhooks,
disnake.Permissions.view_audit_log
]
def load_whitelist(self) -> Dict[str, List[str]]:
"""Load whitelist from JSON file."""
if not os.path.exists(self.whitelist_file):
return {}
try:
with open(self.whitelist_file, "r", encoding="utf-8") as f:
raw_data = json.load(f)
# Convert all user IDs to strings for consistent comparison
result = {}
for guild_id, user_ids in raw_data.items():
result[str(guild_id)] = [str(uid) for uid in user_ids]
return result
except (json.JSONDecodeError, FileNotFoundError):
return {}
def save_whitelist(self, whitelist: Dict[str, List[str]]):
"""Save whitelist to JSON file."""
with open(self.whitelist_file, "w", encoding="utf-8") as f:
json.dump(whitelist, f, ensure_ascii=False, indent=4)
def is_whitelisted(self, guild_id: int, user_id: int) -> bool:
"""Check if user is whitelisted for a specific guild."""
whitelist = self.load_whitelist()
guild_key = str(guild_id)
user_key = str(user_id)
return guild_key in whitelist and user_key in whitelist[guild_key]
def remove_from_whitelist(self, guild_id: int, user_id: int) -> bool:
"""Remove user from whitelist for a specific guild."""
whitelist = self.load_whitelist()
guild_key = str(guild_id)
user_key = str(user_id)
if guild_key in whitelist and user_key in whitelist[guild_key]:
whitelist[guild_key].remove(user_key)
# Clean up empty guild entries
if not whitelist[guild_key]:
del whitelist[guild_key]
self.save_whitelist(whitelist)
return True
return False
def has_big_permissions(self, role: disnake.Role) -> bool:
"""Check if role has significant permissions."""
permissions = role.permissions
for perm in self.big_permissions:
if permissions.administrator or getattr(permissions, perm.name, False):
return True
return False
async def send_whitelist_notification(self, member: disnake.Member, removed_roles: List[disnake.Role]):
"""Send DM notification to user about whitelist member losing roles."""
try:
# Find guild administrators to notify
admins = [m for m in member.guild.members if m.guild_permissions.administrator and not m.bot]
if not admins:
return
# Create embed
embed = disnake.Embed(
title="⚠️ Whitelist Alert",
description=f"Un membre whitelisté a perdu des rôles importants dans **{member.guild.name}**.",
color=disnake.Color.orange(),
timestamp=datetime.now()
)
embed.add_field(
name="👤 Membre concerné",
value=f"{member.mention} ({member.display_name})",
inline=False
)
if removed_roles:
roles_text = "\n".join([f"{role.name}" for role in removed_roles])
embed.add_field(
name="🗑️ Rôles retirés",
value=roles_text,
inline=False
)
embed.add_field(
name="📋 Statut whitelist",
value="✅ Toujours whitelisté",
inline=True
)
# Create view with removal button
view = WhitelistRemovalView(member.id, member.guild.id)
# Send to all admins
for admin in admins:
try:
await admin.send(embed=embed, view=view)
except disnake.Forbidden:
continue # User has DMs disabled
except Exception as e:
print(f"Error sending whitelist notification: {e}")
async def cog_load(self):
"""Register persistent views when cog is loaded"""
await self._register_persistent_views()
async def _register_persistent_views(self):
"""Register all persistent views for guilds with whitelist alerts"""
if self.persistent_views_added:
return
try:
import json
security_file = "data/security_settings.json"
if os.path.exists(security_file):
with open(security_file, "r", encoding="utf-8") as f:
all_settings = json.load(f)
# For each guild with whitelist monitoring, register a default view
# We'll use a generic view that gets recreated when needed
# The key is that each unique custom_id needs to be registered once
view = WhitelistRemovalView(0, 0) # Placeholder with dummy IDs
self.bot.add_view(view)
print(f"[WhitelistMonitor] Persistent view registered")
self.persistent_views_added = True
except Exception as e:
print(f"[WhitelistMonitor] Error registering views: {e}")
@commands.Cog.listener()
async def on_member_update(self, before: disnake.Member, after: disnake.Member):
"""Monitor role changes for whitelist members."""
if before.roles == after.roles:
return
if not self.is_whitelisted(after.guild.id, after.id):
return
# Find removed roles
removed_roles = [role for role in before.roles if role not in after.roles]
# Check if any removed roles had big permissions
important_removed = [role for role in removed_roles if self.has_big_permissions(role)]
if important_removed:
await self.send_whitelist_notification(after, important_removed)
@commands.Cog.listener()
async def on_member_remove(self, member: disnake.Member):
"""Auto-remove from whitelist when member leaves server."""
if self.is_whitelisted(member.guild.id, member.id):
removed = self.remove_from_whitelist(member.guild.id, member.id)
if removed:
# Log the removal
try:
log_channel_id = self.get_log_channel(member.guild.id)
if log_channel_id:
log_channel = member.guild.get_channel(log_channel_id)
if log_channel:
embed = disnake.Embed(
title="🗑️ Auto-Whitelist Removal",
description=f"{member.mention} a été retiré automatiquement de la whitelist après avoir quitté le serveur.",
color=disnake.Color.red(),
timestamp=datetime.now()
)
await log_channel.send(embed=embed)
except Exception as e:
print(f"Error logging whitelist removal: {e}")
def get_log_channel(self, guild_id: int) -> Optional[int]:
"""Get log channel ID from security settings."""
try:
security_file = "data/security_settings.json"
if os.path.exists(security_file):
with open(security_file, "r") as f:
settings = json.load(f)
return settings.get(str(guild_id), {}).get("log_channel_id")
except:
pass
return None
class WhitelistRemovalView(View):
"""View with button to remove user from whitelist."""
def __init__(self, user_id: int, guild_id: int):
super().__init__(timeout=None) # Persistent view
self.user_id = user_id
self.guild_id = guild_id
@disnake.ui.button(
label="Retirer de la whitelist",
style=disnake.ButtonStyle.red,
emoji="🗑️",
custom_id="whitelist_remove_button"
)
async def remove_whitelist(
self,
interaction: disnake.Interaction,
button: Button
):
"""Remove user from whitelist when button is clicked."""
try:
# Get the cog instance
cog = self.bot.get_cog("WhitelistMonitor")
if not cog:
await interaction.response.send_message(
"❌ Erreur: Système de whitelist non disponible.",
ephemeral=True
)
return
# Remove from whitelist
removed = cog.remove_from_whitelist(self.guild_id, self.user_id)
if removed:
# Get user info
try:
user = await self.bot.fetch_user(self.user_id)
user_mention = user.mention
except:
user_mention = f"<@{self.user_id}>"
# Update button
button.disabled = True
button.label = "✅ Retiré"
button.style = disnake.ButtonStyle.green
await interaction.response.edit_message(view=self)
# Send confirmation
embed = disnake.Embed(
title="✅ Retrait effectué",
description=f"{user_mention} a été retiré de la whitelist.",
color=disnake.Color.green()
)
await interaction.followup.send(embed=embed, ephemeral=True)
# Log the removal
try:
guild = self.bot.get_guild(self.guild_id)
if guild:
log_channel_id = cog.get_log_channel(self.guild_id)
if log_channel_id:
log_channel = guild.get_channel(log_channel_id)
if log_channel:
embed = disnake.Embed(
title="🗑️ Whitelist Manual Removal",
description=f"{user_mention} a été retiré manuellement de la whitelist.",
color=disnake.Color.green(),
timestamp=datetime.now()
)
embed.set_footer(text=f"Retiré par {interaction.user.display_name}")
await log_channel.send(embed=embed)
except Exception as e:
print(f"Error logging manual removal: {e}")
else:
await interaction.response.send_message(
" L'utilisateur n'était pas dans la whitelist.",
ephemeral=True
)
except Exception as e:
await interaction.response.send_message(
f"❌ Erreur lors du retrait: {str(e)}",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(WhitelistMonitor(bot))