Mise à jour du système de whitelist, sécurité et logging
This commit is contained in:
parent
d82f704c75
commit
1e384032be
7 changed files with 199 additions and 145 deletions
|
|
@ -34,7 +34,7 @@ class AdvancedLogger:
|
|||
"details": details or {}
|
||||
}
|
||||
|
||||
self._write_log("messages", log_data)
|
||||
asyncio.create_task(self._write_log_async("messages", log_data))
|
||||
|
||||
def log_role_event(self, member: discord.Member, role: discord.Role, event_type: str):
|
||||
"""Log role changes (added/removed)"""
|
||||
|
|
@ -49,7 +49,7 @@ class AdvancedLogger:
|
|||
"role_permissions": role.permissions.value
|
||||
}
|
||||
|
||||
self._write_log("roles", log_data)
|
||||
asyncio.create_task(self._write_log_async("roles", log_data))
|
||||
|
||||
def log_voice_event(self, member: discord.Member, channel: discord.VoiceChannel, event_type: str):
|
||||
"""Log voice channel events (join/leave)"""
|
||||
|
|
@ -63,7 +63,7 @@ class AdvancedLogger:
|
|||
"channel_type": str(channel.type)
|
||||
}
|
||||
|
||||
self._write_log("voice", log_data)
|
||||
asyncio.create_task(self._write_log_async("voice", log_data))
|
||||
|
||||
def log_member_leave(self, member: discord.Member):
|
||||
"""Log when a member leaves the server"""
|
||||
|
|
@ -85,10 +85,10 @@ class AdvancedLogger:
|
|||
}
|
||||
|
||||
# Save to member-specific log
|
||||
self._write_member_log(member.id, log_data)
|
||||
asyncio.create_task(self._write_member_log_async(member.id, log_data))
|
||||
|
||||
# Also save to general logs
|
||||
self._write_log("member_events", log_data)
|
||||
asyncio.create_task(self._write_log_async("member_events", log_data))
|
||||
|
||||
def log_member_join(self, member: discord.Member):
|
||||
"""Log when a member joins the server"""
|
||||
|
|
@ -101,10 +101,13 @@ class AdvancedLogger:
|
|||
"member_avatar": str(member.avatar.url) if member.avatar else None
|
||||
}
|
||||
|
||||
self._write_log("member_events", log_data)
|
||||
asyncio.create_task(self._write_log_async("member_events", log_data))
|
||||
|
||||
def check_previous_roles(self, member_id: int) -> Optional[Dict]:
|
||||
"""Check if member has previous role data when rejoining"""
|
||||
# This one stays synchronous for now as it's called in on_member_join and needs immediate result
|
||||
# but since it only reads ONE file, it's less problematic than writing to cumulative logs.
|
||||
# Format update: it now needs to read NDJSON.
|
||||
member_log_file = f"{self.member_logs_dir}/{member_id}.json"
|
||||
|
||||
if not os.path.exists(member_log_file):
|
||||
|
|
@ -112,7 +115,8 @@ class AdvancedLogger:
|
|||
|
||||
try:
|
||||
with open(member_log_file, 'r', encoding='utf-8') as f:
|
||||
logs = json.load(f)
|
||||
# Read line by line for NDJSON
|
||||
logs = [json.loads(line) for line in f if line.strip()]
|
||||
|
||||
# Find the most recent leave event
|
||||
leave_events = [log for log in logs if log.get("event_type") == "member_leave"]
|
||||
|
|
@ -123,71 +127,39 @@ class AdvancedLogger:
|
|||
kuby_logger.error(f"Error checking previous roles for {member_id}: {e}")
|
||||
|
||||
return None
|
||||
|
||||
def _write_log(self, log_type: str, data: Dict):
|
||||
"""Write log data to appropriate file safely with atomic write"""
|
||||
date_str = datetime.now().strftime("%Y-%m-%d")
|
||||
log_file = f"{self.logs_dir}/{log_type}_{date_str}.json"
|
||||
temp_file = f"{log_file}.tmp"
|
||||
|
||||
existing_logs = []
|
||||
|
||||
async def _write_log_async(self, log_type: str, data: Dict):
|
||||
"""Asynchronous wrapper for log writing"""
|
||||
return await asyncio.to_thread(self._write_log_sync, log_type, data)
|
||||
|
||||
async def _write_member_log_async(self, member_id: int, data: Dict):
|
||||
"""Asynchronous wrapper for member log writing"""
|
||||
return await asyncio.to_thread(self._write_member_log_sync, member_id, data)
|
||||
|
||||
def _write_log_sync(self, log_type: str, data: Dict):
|
||||
"""Write log data to appropriate file using NDJSON (Append-only)"""
|
||||
try:
|
||||
if os.path.exists(log_file):
|
||||
with open(log_file, 'r', encoding='utf-8') as f:
|
||||
try:
|
||||
existing_logs = json.load(f)
|
||||
except json.JSONDecodeError as e:
|
||||
# Handle corrupted file by backing it up and starting fresh
|
||||
corrupted_backup = f"{log_file}.corrupted"
|
||||
os.rename(log_file, corrupted_backup)
|
||||
kuby_logger.warning(f"Corrupted log file detected and backed up to {corrupted_backup}: {e}. Starting new log.")
|
||||
existing_logs = []
|
||||
date_str = datetime.now().strftime("%Y-%m-%d")
|
||||
log_file = f"{self.logs_dir}/{log_type}_{date_str}.json"
|
||||
|
||||
existing_logs.append(data)
|
||||
|
||||
# Atomic write using a temporary file
|
||||
with open(temp_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(existing_logs, f, indent=2, ensure_ascii=False)
|
||||
|
||||
os.replace(temp_file, log_file)
|
||||
# Simple append in NDJSON format
|
||||
with open(log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(json.dumps(data, ensure_ascii=False) + "\n")
|
||||
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"Error writing log for {log_type}: {e}")
|
||||
if os.path.exists(temp_file):
|
||||
try: os.remove(temp_file)
|
||||
except: pass
|
||||
# We don't use kuby_logger here to avoid infinite recursion if nested
|
||||
print(f"CRITICAL ERROR in AdvancedLogger._write_log_sync: {e}")
|
||||
|
||||
def _write_member_log(self, member_id: int, data: Dict):
|
||||
"""Write member-specific log safely with atomic write"""
|
||||
member_log_file = f"{self.member_logs_dir}/{member_id}.json"
|
||||
temp_file = f"{member_log_file}.tmp"
|
||||
|
||||
existing_logs = []
|
||||
def _write_member_log_sync(self, member_id: int, data: Dict):
|
||||
"""Write member-specific log using NDJSON (Append-only)"""
|
||||
try:
|
||||
if os.path.exists(member_log_file):
|
||||
with open(member_log_file, 'r', encoding='utf-8') as f:
|
||||
try:
|
||||
existing_logs = json.load(f)
|
||||
except json.JSONDecodeError as e:
|
||||
# Handle corrupted file by backing it up and starting fresh
|
||||
corrupted_backup = f"{member_log_file}.corrupted"
|
||||
os.rename(member_log_file, corrupted_backup)
|
||||
kuby_logger.warning(f"Corrupted member log detected and backed up to {corrupted_backup}: {e}. Starting new log.")
|
||||
existing_logs = []
|
||||
member_log_file = f"{self.member_logs_dir}/{member_id}.json"
|
||||
|
||||
existing_logs.append(data)
|
||||
|
||||
# Atomic write using a temporary file
|
||||
with open(temp_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(existing_logs, f, indent=2, ensure_ascii=False)
|
||||
|
||||
os.replace(temp_file, member_log_file)
|
||||
with open(member_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(json.dumps(data, ensure_ascii=False) + "\n")
|
||||
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"Error writing member log for {member_id}: {e}")
|
||||
if os.path.exists(temp_file):
|
||||
try: os.remove(temp_file)
|
||||
except: pass
|
||||
print(f"CRITICAL ERROR in AdvancedLogger._write_member_log_sync: {e}")
|
||||
|
||||
async def send_leave_notification(self, member: discord.Member, channel: discord.TextChannel):
|
||||
"""Send notification when member leaves"""
|
||||
|
|
|
|||
|
|
@ -188,10 +188,10 @@ async def generate_servers_json(bot: discord.Client) -> dict:
|
|||
entry["verificationLevel"] = str(guild.verification_level)
|
||||
|
||||
if fields.get("memberCount"):
|
||||
try:
|
||||
members = [m async for m in guild.fetch_members(limit=None)]
|
||||
entry["memberCount"] = sum(1 for m in members if not m.bot)
|
||||
except Exception:
|
||||
# Use cache if available (intents.members is True), otherwise fallback to count
|
||||
if guild.members:
|
||||
entry["memberCount"] = sum(1 for m in guild.members if not m.bot)
|
||||
else:
|
||||
entry["memberCount"] = guild.member_count
|
||||
|
||||
if fields.get("owner"):
|
||||
|
|
@ -209,22 +209,27 @@ async def generate_servers_json(bot: discord.Client) -> dict:
|
|||
pass
|
||||
|
||||
# Membres avec un rôle dont le nom correspond à founderRoles
|
||||
try:
|
||||
async for member in guild.fetch_members(limit=None):
|
||||
if member.bot or member.id == guild.owner_id:
|
||||
continue
|
||||
for role in member.roles:
|
||||
if any(key in role.name.lower() for key in founder_keys):
|
||||
founders.append({
|
||||
"id": str(member.id),
|
||||
"username": member.name,
|
||||
"displayName": member.display_name,
|
||||
"avatar": str(member.display_avatar.url),
|
||||
"isOwner": False,
|
||||
})
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
# Use cache if possible
|
||||
members_to_check = guild.members if guild.members else []
|
||||
if not members_to_check:
|
||||
try:
|
||||
members_to_check = [m async for m in guild.fetch_members(limit=None)]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for member in members_to_check:
|
||||
if member.bot or member.id == guild.owner_id:
|
||||
continue
|
||||
for role in member.roles:
|
||||
if any(key in role.name.lower() for key in founder_keys):
|
||||
founders.append({
|
||||
"id": str(member.id),
|
||||
"username": member.name,
|
||||
"displayName": member.display_name,
|
||||
"avatar": str(member.display_avatar.url),
|
||||
"isOwner": False,
|
||||
})
|
||||
break
|
||||
|
||||
entry["founders"] = founders
|
||||
|
||||
|
|
@ -259,7 +264,14 @@ async def generate_members_json(bot: discord.Client, guild_id: Optional[int] = N
|
|||
raise ValueError(f"Serveur introuvable : {target_id}")
|
||||
|
||||
members_list = []
|
||||
async for member in guild.fetch_members(limit=None):
|
||||
# Use cached members if available, or fetch if not
|
||||
source = guild.members if guild.members else []
|
||||
if not source:
|
||||
async for m in guild.fetch_members(limit=None):
|
||||
if not m.bot:
|
||||
source.append(m)
|
||||
|
||||
for member in source:
|
||||
if member.bot:
|
||||
continue
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue