The begining
This commit is contained in:
commit
dee311e709
71 changed files with 8349 additions and 0 deletions
198
src/advanced_logger.py
Executable file
198
src/advanced_logger.py
Executable file
|
|
@ -0,0 +1,198 @@
|
|||
import discord
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, List
|
||||
from src.logger import kuby_logger
|
||||
|
||||
class AdvancedLogger:
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.logs_dir = "data/logs"
|
||||
self.member_logs_dir = "data/member_logs"
|
||||
self.ensure_directories()
|
||||
|
||||
def ensure_directories(self):
|
||||
"""Ensure log directories exist"""
|
||||
os.makedirs(self.logs_dir, exist_ok=True)
|
||||
os.makedirs(self.member_logs_dir, exist_ok=True)
|
||||
|
||||
def log_message_event(self, message: discord.Message, event_type: str, details: Dict = None):
|
||||
"""Log message events (sent, edited, deleted)"""
|
||||
log_data = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"event_type": event_type,
|
||||
"message_id": message.id,
|
||||
"channel_id": message.channel.id,
|
||||
"channel_name": str(message.channel),
|
||||
"author_id": message.author.id,
|
||||
"author_name": str(message.author),
|
||||
"author_roles": [role.id for role in message.author.roles] if hasattr(message.author, 'roles') else [],
|
||||
"content": message.content,
|
||||
"attachments": [att.url for att in message.attachments],
|
||||
"embeds": [embed.to_dict() for embed in message.embeds],
|
||||
"details": details or {}
|
||||
}
|
||||
|
||||
self._write_log("messages", log_data)
|
||||
|
||||
def log_role_event(self, member: discord.Member, role: discord.Role, event_type: str):
|
||||
"""Log role changes (added/removed)"""
|
||||
log_data = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"event_type": event_type,
|
||||
"member_id": member.id,
|
||||
"member_name": str(member),
|
||||
"role_id": role.id,
|
||||
"role_name": role.name,
|
||||
"role_color": str(role.color),
|
||||
"role_permissions": role.permissions.value
|
||||
}
|
||||
|
||||
self._write_log("roles", log_data)
|
||||
|
||||
def log_voice_event(self, member: discord.Member, channel: discord.VoiceChannel, event_type: str):
|
||||
"""Log voice channel events (join/leave)"""
|
||||
log_data = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"event_type": event_type,
|
||||
"member_id": member.id,
|
||||
"member_name": str(member),
|
||||
"channel_id": channel.id,
|
||||
"channel_name": channel.name,
|
||||
"channel_type": str(channel.type)
|
||||
}
|
||||
|
||||
self._write_log("voice", log_data)
|
||||
|
||||
def log_member_leave(self, member: discord.Member):
|
||||
"""Log when a member leaves the server"""
|
||||
roles_data = {
|
||||
"role_ids": [role.id for role in member.roles if role.name != "@everyone"],
|
||||
"role_names": [role.name for role in member.roles if role.name != "@everyone"],
|
||||
"permissions": [role.permissions.value for role in member.roles if role.name != "@everyone"]
|
||||
}
|
||||
|
||||
log_data = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"event_type": "member_leave",
|
||||
"member_id": member.id,
|
||||
"member_name": str(member),
|
||||
"member_discriminator": member.discriminator,
|
||||
"member_avatar": str(member.avatar.url) if member.avatar else None,
|
||||
"roles": roles_data,
|
||||
"joined_at": member.joined_at.isoformat() if member.joined_at else None
|
||||
}
|
||||
|
||||
# Save to member-specific log
|
||||
self._write_member_log(member.id, log_data)
|
||||
|
||||
# Also save to general logs
|
||||
self._write_log("member_events", log_data)
|
||||
|
||||
def log_member_join(self, member: discord.Member):
|
||||
"""Log when a member joins the server"""
|
||||
log_data = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"event_type": "member_join",
|
||||
"member_id": member.id,
|
||||
"member_name": str(member),
|
||||
"member_discriminator": member.discriminator,
|
||||
"member_avatar": str(member.avatar.url) if member.avatar else None
|
||||
}
|
||||
|
||||
self._write_log("member_events", log_data)
|
||||
|
||||
def check_previous_roles(self, member_id: int) -> Optional[Dict]:
|
||||
"""Check if member has previous role data when rejoining"""
|
||||
member_log_file = f"{self.member_logs_dir}/{member_id}.json"
|
||||
|
||||
if not os.path.exists(member_log_file):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(member_log_file, 'r', encoding='utf-8') as f:
|
||||
logs = json.load(f)
|
||||
|
||||
# Find the most recent leave event
|
||||
leave_events = [log for log in logs if log.get("event_type") == "member_leave"]
|
||||
if leave_events:
|
||||
return leave_events[-1].get("roles", {})
|
||||
|
||||
except Exception as e:
|
||||
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"""
|
||||
date_str = datetime.now().strftime("%Y-%m-%d")
|
||||
log_file = f"{self.logs_dir}/{log_type}_{date_str}.json"
|
||||
|
||||
try:
|
||||
# Append to existing file or create new
|
||||
if os.path.exists(log_file):
|
||||
with open(log_file, 'r', encoding='utf-8') as f:
|
||||
existing_logs = json.load(f)
|
||||
else:
|
||||
existing_logs = []
|
||||
|
||||
existing_logs.append(data)
|
||||
|
||||
with open(log_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(existing_logs, f, indent=2, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"Error writing log for {log_type}: {e}")
|
||||
|
||||
def _write_member_log(self, member_id: int, data: Dict):
|
||||
"""Write member-specific log"""
|
||||
member_log_file = f"{self.member_logs_dir}/{member_id}.json"
|
||||
|
||||
try:
|
||||
# Append to existing file or create new
|
||||
if os.path.exists(member_log_file):
|
||||
with open(member_log_file, 'r', encoding='utf-8') as f:
|
||||
existing_logs = json.load(f)
|
||||
else:
|
||||
existing_logs = []
|
||||
|
||||
existing_logs.append(data)
|
||||
|
||||
with open(member_log_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(existing_logs, f, indent=2, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"Error writing member log for {member_id}: {e}")
|
||||
|
||||
async def send_leave_notification(self, member: discord.Member, channel: discord.TextChannel):
|
||||
"""Send notification when member leaves"""
|
||||
embed = discord.Embed(
|
||||
title="👋 Membre parti",
|
||||
description=f"{member.mention} ({member.name}#{member.discriminator}) a quitté le serveur.",
|
||||
color=discord.Color.red(),
|
||||
timestamp=datetime.now()
|
||||
)
|
||||
|
||||
embed.add_field(
|
||||
name="ID",
|
||||
value=str(member.id),
|
||||
inline=True
|
||||
)
|
||||
|
||||
roles = [role for role in member.roles if role.name != "@everyone"]
|
||||
if roles:
|
||||
embed.add_field(
|
||||
name="Rôles",
|
||||
value=", ".join([role.mention for role in roles]),
|
||||
inline=False
|
||||
)
|
||||
|
||||
embed.set_footer(text=f"ID: {member.id}")
|
||||
|
||||
try:
|
||||
await channel.send(embed=embed)
|
||||
return True
|
||||
except Exception as e:
|
||||
kuby_logger.error(f"Error sending leave notification: {e}")
|
||||
return False
|
||||
113
src/logger.py
Executable file
113
src/logger.py
Executable file
|
|
@ -0,0 +1,113 @@
|
|||
import logging
|
||||
import sys
|
||||
import time
|
||||
import functools
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import Any, Callable
|
||||
import traceback
|
||||
|
||||
class KubyLogger:
|
||||
def __init__(self, name="KubyBot"):
|
||||
self.logger = logging.getLogger(name)
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Créer le format de log
|
||||
formatter = logging.Formatter(
|
||||
'[%(asctime)s] [%(levelname)s] [%(name)s] [%(funcName)s:%(lineno)d] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
|
||||
# Console handler
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
console_handler.setFormatter(formatter)
|
||||
console_handler.setLevel(logging.DEBUG)
|
||||
self.logger.addHandler(console_handler)
|
||||
|
||||
# File handler
|
||||
file_handler = logging.FileHandler('kuby_logs.log')
|
||||
file_handler.setFormatter(formatter)
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
self.logger.addHandler(file_handler)
|
||||
|
||||
# Error file handler
|
||||
error_handler = logging.FileHandler('kuby_errors.log')
|
||||
error_handler.setLevel(logging.ERROR)
|
||||
error_handler.setFormatter(formatter)
|
||||
self.logger.addHandler(error_handler)
|
||||
|
||||
# Audit file handler for security events
|
||||
audit_handler = logging.FileHandler('kuby_audit.log')
|
||||
audit_handler.setLevel(logging.INFO)
|
||||
audit_formatter = logging.Formatter(
|
||||
'[AUDIT] [%(asctime)s] [%(levelname)s] [%(funcName)s] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
audit_handler.setFormatter(audit_formatter)
|
||||
self.logger.addHandler(audit_handler)
|
||||
|
||||
def debug(self, message, **kwargs):
|
||||
self.logger.debug(message, **kwargs)
|
||||
|
||||
def info(self, message, **kwargs):
|
||||
self.logger.info(message, **kwargs)
|
||||
|
||||
def warning(self, message, **kwargs):
|
||||
self.logger.warning(message, **kwargs)
|
||||
|
||||
def error(self, message, **kwargs):
|
||||
self.logger.error(message, **kwargs)
|
||||
|
||||
def critical(self, message, **kwargs):
|
||||
self.logger.critical(message, **kwargs)
|
||||
|
||||
def audit(self, message, **kwargs):
|
||||
"""Special audit logging for security events"""
|
||||
self.logger.log(25, f"AUDIT: {message}", **kwargs)
|
||||
|
||||
def performance(self, message, duration=None, **kwargs):
|
||||
"""Performance logging with timing information"""
|
||||
if duration is not None:
|
||||
self.logger.info(f"PERFORMANCE: {message} - Duration: {duration:.3f}s", **kwargs)
|
||||
else:
|
||||
self.logger.info(f"PERFORMANCE: {message}", **kwargs)
|
||||
|
||||
# Performance decorator
|
||||
def log_performance(func: Callable) -> Callable:
|
||||
"""Decorator to log function performance"""
|
||||
@functools.wraps(func)
|
||||
async def async_wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
kuby_logger.debug(f"Entering {func.__name__} with args: {args}, kwargs: {kwargs}")
|
||||
try:
|
||||
result = await func(*args, **kwargs)
|
||||
duration = time.time() - start_time
|
||||
kuby_logger.performance(f"Completed {func.__name__}", duration=duration)
|
||||
return result
|
||||
except Exception as e:
|
||||
duration = time.time() - start_time
|
||||
kuby_logger.error(f"Error in {func.__name__} after {duration:.3f}s: {str(e)}")
|
||||
kuby_logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
@functools.wraps(func)
|
||||
def sync_wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
kuby_logger.debug(f"Entering {func.__name__} with args: {args}, kwargs: {kwargs}")
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
duration = time.time() - start_time
|
||||
kuby_logger.performance(f"Completed {func.__name__}", duration=duration)
|
||||
return result
|
||||
except Exception as e:
|
||||
duration = time.time() - start_time
|
||||
kuby_logger.error(f"Error in {func.__name__} after {duration:.3f}s: {str(e)}")
|
||||
kuby_logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
return async_wrapper
|
||||
return sync_wrapper
|
||||
|
||||
# Instance globale du logger
|
||||
kuby_logger = KubyLogger()
|
||||
Loading…
Add table
Add a link
Reference in a new issue