Initialisation du repository de Beta
This commit is contained in:
commit
14985f6dbb
9469 changed files with 1903273 additions and 0 deletions
BIN
commandes/autres/__pycache__/config.cpython-311.pyc
Normal file
BIN
commandes/autres/__pycache__/config.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/config.cpython-312.pyc
Normal file
BIN
commandes/autres/__pycache__/config.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/logger.cpython-311.pyc
Normal file
BIN
commandes/autres/__pycache__/logger.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/logger.cpython-312.pyc
Normal file
BIN
commandes/autres/__pycache__/logger.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/ping.cpython-311.pyc
Normal file
BIN
commandes/autres/__pycache__/ping.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/ping.cpython-312.pyc
Normal file
BIN
commandes/autres/__pycache__/ping.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/send_message.cpython-311.pyc
Normal file
BIN
commandes/autres/__pycache__/send_message.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/send_message.cpython-312.pyc
Normal file
BIN
commandes/autres/__pycache__/send_message.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/setgoodbyechannel.cpython-311.pyc
Normal file
BIN
commandes/autres/__pycache__/setgoodbyechannel.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/setgoodbyechannel.cpython-312.pyc
Normal file
BIN
commandes/autres/__pycache__/setgoodbyechannel.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/setmuterole.cpython-311.pyc
Normal file
BIN
commandes/autres/__pycache__/setmuterole.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/setwelcomechannel.cpython-311.pyc
Normal file
BIN
commandes/autres/__pycache__/setwelcomechannel.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/setwelcomechannel.cpython-312.pyc
Normal file
BIN
commandes/autres/__pycache__/setwelcomechannel.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/autres/__pycache__/utils.cpython-312.pyc
Normal file
BIN
commandes/autres/__pycache__/utils.cpython-312.pyc
Normal file
Binary file not shown.
91
commandes/autres/config.py
Normal file
91
commandes/autres/config.py
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import os
|
||||
import json
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
class Config:
|
||||
def __init__(self, config_file: str = 'config.json'):
|
||||
self.config_file = config_file
|
||||
self.data: Dict[str, Any] = {}
|
||||
self.load_config()
|
||||
|
||||
def load_config(self):
|
||||
"""Load configuration from file if it exists."""
|
||||
if os.path.exists(self.config_file):
|
||||
try:
|
||||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||||
self.data = json.load(f)
|
||||
except (json.JSONDecodeError, FileNotFoundError):
|
||||
self.data = {}
|
||||
else:
|
||||
self.data = {}
|
||||
|
||||
def save_config(self):
|
||||
"""Save configuration to file if dirty."""
|
||||
if self.dirty:
|
||||
try:
|
||||
with open(self.config_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(self.data, f, indent=4, ensure_ascii=False)
|
||||
self.dirty = False
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de la sauvegarde de la configuration: {e}")
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
"""Get a configuration value."""
|
||||
return self.data.get(key, default)
|
||||
|
||||
def set(self, key: str, value: Any):
|
||||
"""Set a configuration value and save."""
|
||||
self.data[key] = value
|
||||
self.save_config()
|
||||
|
||||
def get_welcome_channel(self, guild_id: int) -> Optional[int]:
|
||||
"""Get welcome channel ID for a guild."""
|
||||
return self.get('welcome_channels', {}).get(str(guild_id))
|
||||
|
||||
def set_welcome_channel(self, guild_id: int, channel_id: int):
|
||||
"""Set welcome channel for a guild."""
|
||||
welcome_channels = self.get('welcome_channels', {})
|
||||
welcome_channels[str(guild_id)] = channel_id
|
||||
self.set('welcome_channels', welcome_channels)
|
||||
|
||||
def get_goodbye_channel(self, guild_id: int) -> Optional[int]:
|
||||
"""Get goodbye channel ID for a guild."""
|
||||
return self.get('goodbye_channels', {}).get(str(guild_id))
|
||||
|
||||
def set_goodbye_channel(self, guild_id: int, channel_id: int):
|
||||
"""Set goodbye channel for a guild."""
|
||||
goodbye_channels = self.get('goodbye_channels', {})
|
||||
goodbye_channels[str(guild_id)] = channel_id
|
||||
self.set('goodbye_channels', goodbye_channels)
|
||||
|
||||
def get_mute_role(self, guild_id: int) -> Optional[str]:
|
||||
"""Get mute role name for a guild."""
|
||||
return self.get('mute_roles', {}).get(str(guild_id))
|
||||
|
||||
def set_mute_role(self, guild_id: int, role_name: str):
|
||||
"""Set mute role for a guild."""
|
||||
mute_roles = self.get('mute_roles', {})
|
||||
mute_roles[str(guild_id)] = role_name
|
||||
self.set('mute_roles', mute_roles)
|
||||
self.save_config() # Force save for critical config
|
||||
|
||||
def get_allowed_words(self) -> list:
|
||||
"""Get list of allowed words."""
|
||||
return self.get('allowed_words', [])
|
||||
|
||||
def set_allowed_words(self, words: list):
|
||||
"""Set allowed words."""
|
||||
self.set('allowed_words', words)
|
||||
|
||||
def get_mod_report_channel(self, guild_id: int) -> Optional[int]:
|
||||
"""Get mod report channel ID for a guild."""
|
||||
return self.get('mod_report_channels', {}).get(str(guild_id))
|
||||
|
||||
def set_mod_report_channel(self, guild_id: int, channel_id: int):
|
||||
"""Set mod report channel for a guild."""
|
||||
mod_channels = self.get('mod_report_channels', {})
|
||||
mod_channels[str(guild_id)] = channel_id
|
||||
self.set('mod_report_channels', mod_channels)
|
||||
|
||||
# Global config instance
|
||||
config = Config()
|
||||
65
commandes/autres/help.py
Normal file
65
commandes/autres/help.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import discord
|
||||
import os
|
||||
|
||||
HELP_TEXT = """
|
||||
**Le Superviseur** - Bot de modération Discord
|
||||
|
||||
**Commandes générales:**
|
||||
• `@Le Superviseur {message}` - Parler avec l'IA (répondez à ses messages aussi)
|
||||
• `!help` - Afficher cette aide
|
||||
• `!status` - Afficher le statut du bot
|
||||
|
||||
**Modération:**
|
||||
• `!kick user [raison]` - Expulser un utilisateur
|
||||
• `!ban user [raison]` - Bannir un utilisateur
|
||||
• `!unban user` - Débannir un utilisateur
|
||||
• `!warn user [raison]` - Avertir un utilisateur
|
||||
• `!list_warnings` - Lister les avertissements
|
||||
• `!mute user duration` - Muter temporairement
|
||||
• `!timeout user duration` - Timeout temporaire
|
||||
• `!purge amount` - Supprimer des messages
|
||||
|
||||
**Gestion des salons:**
|
||||
• `!create_channel name1 [name2 name3 ...] [category type]` - Créer plusieurs salons (type: textuel/vocal, optionnel)
|
||||
• `!delete_channel channel1 [channel2 ...]` - Supprimer plusieurs salons
|
||||
• `!modify_channel channel new_name [new_category] [topic] [user_limit]` - Modifier un salon par session (pour multi, répétez la commande)
|
||||
• `!move_channel channel1 new_category [channel2 new_category ...]` - Déplacer plusieurs salons vers une nouvelle catégorie
|
||||
• `!rename_channel channel1 new_name [channel2 new_name ...]` - Renommer plusieurs salons
|
||||
|
||||
**Gestion des catégories:**
|
||||
• `!create_category name1 [name2 ...]` - Créer plusieurs catégories
|
||||
• `!delete_category category1 [category2 ...]` - Supprimer plusieurs catégories
|
||||
• `!modify_category category new_name` - Modifier une catégorie par commande (pour multi, répétez)
|
||||
• `!move_category category1 position [category2 position ...]` - Déplacer plusieurs catégories
|
||||
• `!rename_category category1 new_name [category2 new_name ...]` - Renommer plusieurs catégories
|
||||
|
||||
**Gestion des rôles:**
|
||||
• `!create_role name [color] [permissions]` - Créer un rôle par commande (pour multi, répétez)
|
||||
• `!delete_role role1 [role2 ...]` - Supprimer plusieurs rôles
|
||||
• `!modify_role role new_name [new_color] [new_permissions]` - Modifier un rôle par commande (pour multi, répétez)
|
||||
• `!add_role_to_user user role1 [role2 ...]` - Donner plusieurs rôles à un utilisateur
|
||||
• `!remove_role_from_user user role1 [role2 ...]` - Retirer plusieurs rôles d'un utilisateur
|
||||
• `!move_role role1 position [role2 position ...]` - Déplacer plusieurs rôles
|
||||
• `!rename_role role1 new_name [role2 new_name ...]` - Renommer plusieurs rôles
|
||||
|
||||
**Configuration:**
|
||||
• `!set_welcome_channel channel` - Canal de bienvenue
|
||||
• `!set_goodbye_channel channel` - Canal d'au revoir
|
||||
• `!set_mute_role role` - Rôle muet
|
||||
|
||||
**Outils:**
|
||||
• `!ping` - Vérifier latence
|
||||
|
||||
**Note:** Les commandes supportent maintenant plusieurs cibles à la fois quand applicable. Remplacez `user`, `channel`, etc. par des mentions ou IDs. Les [ ] indiquent paramètres optionnels. Pour créer/modifier multiples salons/rôles avec options différentes, utilisez des commandes séparées ou l'IA.
|
||||
|
||||
Le bot utilise l'IA Ollama pour des réponses intelligentes et peut gérer les salons, rôles et modération efficacement.
|
||||
"""
|
||||
|
||||
async def execute(bot, params, message):
|
||||
embed = discord.Embed(
|
||||
title="Aide du Superviseur",
|
||||
description=HELP_TEXT,
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
embed.set_footer(text="Utilisez les mentions pour référencer utilisateurs/salons/rôles")
|
||||
await message.channel.send(embed=embed)
|
||||
54
commandes/autres/logger.py
Normal file
54
commandes/autres/logger.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import logging
|
||||
import json
|
||||
from datetime import datetime
|
||||
from .config import config
|
||||
|
||||
class ActionLogger:
|
||||
def __init__(self, log_file: str = 'actions.log'):
|
||||
self.log_file = log_file
|
||||
self.logger = logging.getLogger('ActionLogger')
|
||||
self.logger.setLevel(logging.INFO)
|
||||
|
||||
# File handler
|
||||
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
||||
file_handler.setLevel(logging.INFO)
|
||||
|
||||
# Formatter
|
||||
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||
file_handler.setFormatter(formatter)
|
||||
|
||||
# Add handler if not already added
|
||||
if not self.logger.handlers:
|
||||
self.logger.addHandler(file_handler)
|
||||
|
||||
def log_action(self, action: str, user: str, guild: str, params: dict, success: bool, error: str = None):
|
||||
"""Log an action performed by the bot."""
|
||||
log_entry = {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'action': action,
|
||||
'user': user,
|
||||
'guild': guild,
|
||||
'params': params,
|
||||
'success': success,
|
||||
'error': error
|
||||
}
|
||||
|
||||
message = f"ACTION: {json.dumps(log_entry, ensure_ascii=False)}"
|
||||
if success:
|
||||
self.logger.info(message)
|
||||
else:
|
||||
self.logger.error(message)
|
||||
|
||||
def log_message(self, level: str, message: str):
|
||||
"""Log a general message."""
|
||||
if level.casefold() == 'info':
|
||||
self.logger.info(message)
|
||||
elif level.casefold() == 'warning':
|
||||
self.logger.warning(message)
|
||||
elif level.casefold() == 'error':
|
||||
self.logger.error(message)
|
||||
elif level.casefold() == 'debug':
|
||||
self.logger.debug(message)
|
||||
|
||||
# Global logger instance
|
||||
action_logger = ActionLogger()
|
||||
9
commandes/autres/ping.py
Normal file
9
commandes/autres/ping.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import discord
|
||||
import time
|
||||
|
||||
async def execute(bot, params, message):
|
||||
start = time.perf_counter()
|
||||
temp_msg = await message.channel.send("Pong!")
|
||||
end = time.perf_counter()
|
||||
latency = round((end - start) * 1000) # ms
|
||||
await temp_msg.edit(content=f"Pong! Latence : {latency}ms\nLatence API : {round(bot.latency * 1000)}ms")
|
||||
95
commandes/autres/send_message.py
Normal file
95
commandes/autres/send_message.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channel_name = params.get('channel_name')
|
||||
dm_user_mention = params.get('dm_user')
|
||||
content = params.get('message', '')
|
||||
|
||||
if not content.strip():
|
||||
raise Exception("Le message ne peut pas être vide.")
|
||||
|
||||
guild = message.guild
|
||||
sent_count = 0
|
||||
|
||||
if channel_name:
|
||||
# Send to channel
|
||||
# 1. Exact match
|
||||
channel = discord.utils.get(guild.channels, name=channel_name)
|
||||
|
||||
# 2. Fuzzy match if not found (case insensitive, or partial match)
|
||||
if not channel:
|
||||
# Case insensitive
|
||||
channel = discord.utils.find(lambda c: c.name.lower() == channel_name.lower(), guild.channels)
|
||||
|
||||
if not channel:
|
||||
# Partial match (ignoring emojis/special chars often helps)
|
||||
# Normalize: remove non-alphanumeric chars for comparison
|
||||
def normalize(s):
|
||||
return "".join(c for c in s if c.isalnum()).lower()
|
||||
|
||||
target_norm = normalize(channel_name)
|
||||
if target_norm:
|
||||
channel = discord.utils.find(lambda c: normalize(c.name) == target_norm, guild.channels)
|
||||
|
||||
if not channel:
|
||||
# Last resort: contains
|
||||
channel = discord.utils.find(lambda c: channel_name.lower() in c.name.lower(), guild.channels)
|
||||
|
||||
if not channel or not isinstance(channel, discord.TextChannel):
|
||||
raise Exception(f"Salon '{channel_name}' introuvable.")
|
||||
|
||||
# Check permissions
|
||||
if not channel.permissions_for(guild.me).send_messages:
|
||||
raise Exception(f"Le bot n'a pas les permissions pour envoyer des messages dans #{channel.name}.")
|
||||
await bot.messaging.send_message_with_limit(channel, content)
|
||||
sent_count += 1
|
||||
|
||||
if dm_user_mention:
|
||||
# Send DM to user
|
||||
# Parse mention or ID or display name
|
||||
user = None
|
||||
if dm_user_mention.startswith('<@') and dm_user_mention.endswith('>'):
|
||||
# Extract ID from mention
|
||||
user_id_str = dm_user_mention.split('@')[-1].rstrip('>')
|
||||
if user_id_str.isdigit():
|
||||
user_id = int(user_id_str)
|
||||
user = guild.get_member(user_id)
|
||||
if not user:
|
||||
try:
|
||||
user = await guild.fetch_member(user_id)
|
||||
except discord.NotFound:
|
||||
raise Exception(f"Utilisateur introuvable via mention: {dm_user_mention}")
|
||||
elif dm_user_mention.isdigit():
|
||||
user_id = int(dm_user_mention)
|
||||
user = guild.get_member(user_id)
|
||||
if not user:
|
||||
try:
|
||||
user = await guild.fetch_member(user_id)
|
||||
except discord.NotFound:
|
||||
raise Exception(f"Utilisateur introuvable via ID: {dm_user_mention}")
|
||||
else:
|
||||
# Try to find by display name or username
|
||||
user = discord.utils.find(lambda m: m.display_name == dm_user_mention or m.name == dm_user_mention, guild.members)
|
||||
if not user:
|
||||
# Try partial match (case insensitive)
|
||||
dm_user_lower = dm_user_mention.lower()
|
||||
user = discord.utils.find(lambda m: dm_user_lower in m.display_name.lower() or dm_user_lower in m.name.lower(), guild.members)
|
||||
if not user:
|
||||
raise Exception(f"Utilisateur introuvable via nom: {dm_user_mention}")
|
||||
|
||||
try:
|
||||
await user.send(content)
|
||||
sent_count += 1
|
||||
except discord.Forbidden:
|
||||
raise Exception(f"Impossible d'envoyer un DM à {dm_user_mention}. L'utilisateur a peut-être bloqué le bot.")
|
||||
|
||||
if sent_count == 0:
|
||||
raise Exception("Au moins un destinataire (channel_name ou dm_user) doit être spécifié.")
|
||||
|
||||
# If both were specified and successful
|
||||
if channel_name and dm_user_mention and sent_count == 2:
|
||||
await message.channel.send(f"Message envoyé dans #{channel_name} et en DM à {dm_user_mention}.")
|
||||
elif channel_name:
|
||||
await message.channel.send(f"Message envoyé dans #{channel_name}.")
|
||||
elif dm_user_mention:
|
||||
await message.channel.send(f"Message envoyé en DM à {dm_user_mention}.")
|
||||
12
commandes/autres/setgoodbyechannel.py
Normal file
12
commandes/autres/setgoodbyechannel.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import discord
|
||||
from .config import config
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channel_name = params.get('channel_name')
|
||||
guild = message.guild
|
||||
channel = discord.utils.find(lambda c: c.name.casefold() == channel_name.casefold(), guild.channels)
|
||||
if not channel:
|
||||
await message.channel.send(f"Salon '{channel_name}' introuvable.")
|
||||
return
|
||||
config.set_goodbye_channel(guild.id, channel.id)
|
||||
await message.channel.send(f"Salon d'au revoir défini sur {channel.mention}.")
|
||||
14
commandes/autres/setmuterole.py
Normal file
14
commandes/autres/setmuterole.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import discord
|
||||
from .config import config
|
||||
|
||||
async def execute(bot, params, message):
|
||||
role_name = params.get('role_name')
|
||||
if not role_name:
|
||||
await message.channel.send("Veuillez spécifier un nom de rôle.")
|
||||
return
|
||||
role = discord.utils.find(lambda r: r.name.casefold() == role_name.casefold(), message.guild.roles)
|
||||
if not role:
|
||||
await message.channel.send(f"Rôle '{role_name}' introuvable.")
|
||||
return
|
||||
config.set_mute_role(message.guild.id, role.name)
|
||||
await message.channel.send(f"Rôle mute défini sur {role.mention}.")
|
||||
12
commandes/autres/setwelcomechannel.py
Normal file
12
commandes/autres/setwelcomechannel.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import discord
|
||||
from .config import config
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channel_name = params.get('channel_name')
|
||||
guild = message.guild
|
||||
channel = discord.utils.find(lambda c: c.name.casefold() == channel_name.casefold(), guild.channels)
|
||||
if not channel:
|
||||
await message.channel.send(f"Salon '{channel_name}' introuvable.")
|
||||
return
|
||||
config.set_welcome_channel(guild.id, channel.id)
|
||||
await message.channel.send(f"Salon de bienvenue défini sur {channel.mention}.")
|
||||
46
commandes/autres/status.py
Normal file
46
commandes/autres/status.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import discord
|
||||
from datetime import timedelta
|
||||
import time
|
||||
import psutil
|
||||
import os
|
||||
|
||||
async def execute(bot, params, message):
|
||||
embed = discord.Embed(
|
||||
title="Statut du Superviseur",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
|
||||
# Bot info
|
||||
embed.add_field(
|
||||
name="🤖 Bot",
|
||||
value=f"Connecté en tant que {bot.user.mention}\n"
|
||||
f"Serveur principal : {bot.guild_id}\n"
|
||||
f"Latence : {round(bot.latency * 1000)}ms",
|
||||
inline=True
|
||||
)
|
||||
|
||||
# AI/Memory stats
|
||||
metrics = bot.get_metrics()
|
||||
embed.add_field(
|
||||
name="🧠 IA & Mémoire",
|
||||
value=f"Requêtes LLM : {metrics.get('total_llm_requests', 0)}\n"
|
||||
f"Succès : {metrics.get('total_llm_success', 0)}\n"
|
||||
f"Erreurs : {metrics.get('total_llm_errors', 0)}\n"
|
||||
f"Temps moyen : {round(metrics.get('total_llm_time', 0) / max(metrics.get('total_llm_requests', 1), 1), 2)}s",
|
||||
inline=True
|
||||
)
|
||||
|
||||
# System stats
|
||||
process = psutil.Process(os.getpid())
|
||||
memory = process.memory_info().rss / 1024 / 1024 # MB
|
||||
uptime = timedelta(seconds=time.time() - process.create_time())
|
||||
embed.add_field(
|
||||
name="💻 Système",
|
||||
value=f"Mémoire : {round(memory, 2)} MB\n"
|
||||
f"Disponible : {round(psutil.virtual_memory().available / 1024 / 1024, 0)} MB\n"
|
||||
f"Disponible pour bot : {round(psutil.virtual_memory().available / 1024 / 1024 / 1024, 2)} GB",
|
||||
inline=True
|
||||
)
|
||||
|
||||
embed.set_footer(text=f"Uptime: {uptime}")
|
||||
await message.channel.send(embed=embed)
|
||||
34
commandes/autres/utils.py
Normal file
34
commandes/autres/utils.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
"""
|
||||
Utilities for command parsing and common operations
|
||||
"""
|
||||
|
||||
import discord
|
||||
|
||||
async def parse_user_mention(bot, target_mention, message):
|
||||
"""
|
||||
Parse a user mention, username, or ID to get the user ID and member object.
|
||||
Returns (user_id, member) or (None, None) if not found.
|
||||
"""
|
||||
if target_mention.startswith('<@') and target_mention.endswith('>'):
|
||||
user_id = int(target_mention[2:-1]) if not target_mention.startswith('<@!') else int(target_mention[3:-1])
|
||||
elif target_mention.startswith('@'):
|
||||
# Handle @username
|
||||
username = target_mention[1:].lower()
|
||||
member = discord.utils.find(lambda m: username in m.name.lower() or username in m.display_name.lower(), message.guild.members)
|
||||
if not member:
|
||||
return None, None
|
||||
user_id = member.id
|
||||
else:
|
||||
try:
|
||||
user_id = int(target_mention)
|
||||
except ValueError:
|
||||
return None, None
|
||||
|
||||
member = message.guild.get_member(user_id)
|
||||
if not member:
|
||||
try:
|
||||
user = await bot.fetch_user(user_id)
|
||||
return user_id, None # User exists but not in guild
|
||||
except discord.NotFound:
|
||||
return None, None
|
||||
return user_id, member
|
||||
BIN
commandes/categories/__pycache__/creer.cpython-311.pyc
Normal file
BIN
commandes/categories/__pycache__/creer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/categories/__pycache__/creer.cpython-312.pyc
Normal file
BIN
commandes/categories/__pycache__/creer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/categories/__pycache__/deplacer.cpython-311.pyc
Normal file
BIN
commandes/categories/__pycache__/deplacer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/categories/__pycache__/deplacer.cpython-312.pyc
Normal file
BIN
commandes/categories/__pycache__/deplacer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/categories/__pycache__/modifier.cpython-311.pyc
Normal file
BIN
commandes/categories/__pycache__/modifier.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/categories/__pycache__/renommer.cpython-311.pyc
Normal file
BIN
commandes/categories/__pycache__/renommer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/categories/__pycache__/renommer.cpython-312.pyc
Normal file
BIN
commandes/categories/__pycache__/renommer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/categories/__pycache__/supprimer.cpython-311.pyc
Normal file
BIN
commandes/categories/__pycache__/supprimer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/categories/__pycache__/supprimer.cpython-312.pyc
Normal file
BIN
commandes/categories/__pycache__/supprimer.cpython-312.pyc
Normal file
Binary file not shown.
18
commandes/categories/creer.py
Normal file
18
commandes/categories/creer.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
categories = params.get('categories', [])
|
||||
if not categories:
|
||||
# Fallback for single category
|
||||
categories = [params]
|
||||
guild = message.guild
|
||||
created_count = 0
|
||||
for category_params in categories:
|
||||
category_name = category_params.get('category_name')
|
||||
try:
|
||||
category = await guild.create_category(category_name)
|
||||
created_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour créer la catégorie '{category_name}'.")
|
||||
if created_count > 0:
|
||||
await message.channel.send(f"{created_count} catégorie(s) créée(s).")
|
||||
23
commandes/categories/deplacer.py
Normal file
23
commandes/categories/deplacer.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
categories = params.get('categories', [])
|
||||
if not categories:
|
||||
# Fallback for single category
|
||||
categories = [params]
|
||||
guild = message.guild
|
||||
moved_count = 0
|
||||
for category_params in categories:
|
||||
category_name = category_params.get('category_name')
|
||||
position = category_params.get('position')
|
||||
category = discord.utils.find(lambda cat: cat.name.casefold() == category_name.casefold(), guild.categories)
|
||||
if not category:
|
||||
await message.channel.send(f"Catégorie '{category_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await category.edit(position=position)
|
||||
moved_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour déplacer la catégorie '{category_name}'.")
|
||||
if moved_count > 0:
|
||||
await message.channel.send(f"{moved_count} catégorie(s) déplacée(s).")
|
||||
23
commandes/categories/renommer.py
Normal file
23
commandes/categories/renommer.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
categories = params.get('categories', [])
|
||||
if not categories:
|
||||
# Fallback for single category
|
||||
categories = [params]
|
||||
guild = message.guild
|
||||
renamed_count = 0
|
||||
for category_params in categories:
|
||||
category_name = category_params.get('category_name')
|
||||
new_name = category_params.get('new_name')
|
||||
category = discord.utils.find(lambda cat: cat.name.casefold() == category_name.casefold(), guild.categories)
|
||||
if not category:
|
||||
await message.channel.send(f"Catégorie '{category_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await category.edit(name=new_name)
|
||||
renamed_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour renommer la catégorie '{category_name}'.")
|
||||
if renamed_count > 0:
|
||||
await message.channel.send(f"{renamed_count} catégorie(s) renommée(s).")
|
||||
22
commandes/categories/supprimer.py
Normal file
22
commandes/categories/supprimer.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
categories = params.get('categories', [])
|
||||
if not categories:
|
||||
# Fallback for single category
|
||||
categories = [params]
|
||||
guild = message.guild
|
||||
deleted_count = 0
|
||||
for category_params in categories:
|
||||
category_name = category_params.get('category_name')
|
||||
category = discord.utils.find(lambda cat: cat.name.casefold() == category_name.casefold(), guild.categories)
|
||||
if not category:
|
||||
await message.channel.send(f"Catégorie '{category_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await category.delete(reason="Supprimée par le Superviseur")
|
||||
deleted_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour supprimer la catégorie '{category_name}'.")
|
||||
if deleted_count > 0:
|
||||
await message.channel.send(f"{deleted_count} catégorie(s) supprimée(s).")
|
||||
BIN
commandes/roles/__pycache__/add_role.cpython-311.pyc
Normal file
BIN
commandes/roles/__pycache__/add_role.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/add_role.cpython-312.pyc
Normal file
BIN
commandes/roles/__pycache__/add_role.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/creer.cpython-310.pyc
Normal file
BIN
commandes/roles/__pycache__/creer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/creer.cpython-311.pyc
Normal file
BIN
commandes/roles/__pycache__/creer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/creer.cpython-312.pyc
Normal file
BIN
commandes/roles/__pycache__/creer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/deplacer.cpython-310.pyc
Normal file
BIN
commandes/roles/__pycache__/deplacer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/deplacer.cpython-311.pyc
Normal file
BIN
commandes/roles/__pycache__/deplacer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/deplacer.cpython-312.pyc
Normal file
BIN
commandes/roles/__pycache__/deplacer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/modifier.cpython-310.pyc
Normal file
BIN
commandes/roles/__pycache__/modifier.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/modifier.cpython-312.pyc
Normal file
BIN
commandes/roles/__pycache__/modifier.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/remove_role.cpython-311.pyc
Normal file
BIN
commandes/roles/__pycache__/remove_role.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/remove_role.cpython-312.pyc
Normal file
BIN
commandes/roles/__pycache__/remove_role.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/renommer.cpython-310.pyc
Normal file
BIN
commandes/roles/__pycache__/renommer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/renommer.cpython-311.pyc
Normal file
BIN
commandes/roles/__pycache__/renommer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/renommer.cpython-312.pyc
Normal file
BIN
commandes/roles/__pycache__/renommer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/supprimer.cpython-310.pyc
Normal file
BIN
commandes/roles/__pycache__/supprimer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/supprimer.cpython-311.pyc
Normal file
BIN
commandes/roles/__pycache__/supprimer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/roles/__pycache__/supprimer.cpython-312.pyc
Normal file
BIN
commandes/roles/__pycache__/supprimer.cpython-312.pyc
Normal file
Binary file not shown.
62
commandes/roles/add_role.py
Normal file
62
commandes/roles/add_role.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
role_assignments = params.get('role_assignments', [])
|
||||
if not role_assignments:
|
||||
# Fallback for single assignment
|
||||
role_assignments = [params]
|
||||
assigned_count = 0
|
||||
total_members = 0
|
||||
for assignment in role_assignments:
|
||||
target_mention = assignment.get('target_user_mention')
|
||||
role_name = assignment.get('role_name')
|
||||
role = discord.utils.find(lambda r: r.name.casefold() == role_name.casefold(), message.guild.roles)
|
||||
if not role:
|
||||
await message.channel.send(f"Rôle '{role_name}' introuvable.")
|
||||
continue
|
||||
|
||||
# Check for bulk assignment
|
||||
if target_mention.lower() in ['@everyone', 'everyone', 'all', 'tous']:
|
||||
total_members = 0
|
||||
assigned_count = 0
|
||||
await message.channel.send(f"Ajout du rôle '{role.name}' à tous les membres... (Cela peut prendre du temps)")
|
||||
for member in message.guild.members:
|
||||
if member.bot:
|
||||
continue # Skip bots
|
||||
try:
|
||||
await member.add_roles(role, reason=f"Rôle ajouté par le Superviseur à tous les membres")
|
||||
assigned_count += 1
|
||||
total_members += 1
|
||||
if assigned_count % 10 == 0: # Progress update every 10 assigns
|
||||
await message.channel.send(f"Progression: {assigned_count} sur {total_members} membres...")
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Impossible d'ajouter le rôle à {member.mention} (permissions insuffisantes)")
|
||||
continue
|
||||
except Exception as e:
|
||||
await message.channel.send(f"Erreur lors de l'ajout à {member.mention}: {str(e)}")
|
||||
continue
|
||||
await message.channel.send(f"Rôle '{role.name}' ajouté à {assigned_count} membres (bots exclus).")
|
||||
continue
|
||||
|
||||
# Single user assignment
|
||||
if target_mention.startswith('<@') and target_mention.endswith('>'):
|
||||
user_id = int(target_mention[2:-1])
|
||||
elif target_mention.startswith('<@!') and target_mention.endswith('>'):
|
||||
user_id = int(target_mention[3:-1])
|
||||
else:
|
||||
try:
|
||||
user_id = int(target_mention)
|
||||
except ValueError:
|
||||
await message.channel.send(f"Mention ou ID invalide: {target_mention}")
|
||||
continue
|
||||
member = message.guild.get_member(user_id)
|
||||
if not member:
|
||||
await message.channel.send(f"Utilisateur {target_mention} introuvable.")
|
||||
continue
|
||||
try:
|
||||
await member.add_roles(role, reason=f"Rôle ajouté par le Superviseur")
|
||||
assigned_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour ajouter le rôle {role.name} à {member.mention}.")
|
||||
if assigned_count > 0 and not total_members:
|
||||
await message.channel.send(f"{assigned_count} rôle(s) ajouté(s).")
|
||||
32
commandes/roles/creer.py
Normal file
32
commandes/roles/creer.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
roles = params.get('roles', [])
|
||||
if not roles:
|
||||
# Fallback for single role
|
||||
roles = [params]
|
||||
guild = message.guild
|
||||
created_count = 0
|
||||
for role_params in roles:
|
||||
role_name = role_params.get('role_name')
|
||||
color = role_params.get('color', '#ffffff')
|
||||
permissions_str = role_params.get('permissions', '')
|
||||
# Parse color
|
||||
try:
|
||||
color = discord.Color(int(color.lstrip('#'), 16))
|
||||
except ValueError:
|
||||
color = discord.Color.default()
|
||||
# Parse permissions
|
||||
permissions = discord.Permissions()
|
||||
if permissions_str:
|
||||
perm_list = [p.strip() for p in permissions_str.split(',')]
|
||||
for perm in perm_list:
|
||||
if hasattr(discord.Permissions, perm):
|
||||
setattr(permissions, perm, True)
|
||||
try:
|
||||
role = await guild.create_role(name=role_name, color=color, permissions=permissions)
|
||||
created_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour créer le rôle '{role_name}'.")
|
||||
if created_count > 0:
|
||||
await message.channel.send(f"{created_count} rôle(s) créé(s).")
|
||||
23
commandes/roles/deplacer.py
Normal file
23
commandes/roles/deplacer.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
roles = params.get('roles', [])
|
||||
if not roles:
|
||||
# Fallback for single role
|
||||
roles = [params]
|
||||
guild = message.guild
|
||||
moved_count = 0
|
||||
for role_params in roles:
|
||||
role_name = role_params.get('role_name')
|
||||
position = role_params.get('position')
|
||||
role = discord.utils.find(lambda r: r.name.casefold() == role_name.casefold(), guild.roles)
|
||||
if not role:
|
||||
await message.channel.send(f"Rôle '{role_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await role.edit(position=position)
|
||||
moved_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour déplacer le rôle '{role_name}'.")
|
||||
if moved_count > 0:
|
||||
await message.channel.send(f"{moved_count} rôle(s) déplacé(s).")
|
||||
23
commandes/roles/modifier.py
Normal file
23
commandes/roles/modifier.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
roles = params.get('roles', [])
|
||||
if not roles:
|
||||
# Fallback for single role
|
||||
roles = [params]
|
||||
guild = message.guild
|
||||
modified_count = 0
|
||||
for role_params in roles:
|
||||
role_name = role_params.get('role_name')
|
||||
new_name = role_params.get('new_name')
|
||||
role = discord.utils.find(lambda r: r.name.casefold() == role_name.casefold(), guild.roles)
|
||||
if not role:
|
||||
await message.channel.send(f"Rôle '{role_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await role.edit(name=new_name)
|
||||
modified_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour modifier le rôle '{role_name}'.")
|
||||
if modified_count > 0:
|
||||
await message.channel.send(f"{modified_count} rôle(s) modifié(s).")
|
||||
71
commandes/roles/remove_role.py
Normal file
71
commandes/roles/remove_role.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
role_removals = params.get('role_removals', [])
|
||||
if not role_removals:
|
||||
# Fallback for single removal
|
||||
role_removals = [params]
|
||||
removed_count = 0
|
||||
total_members = 0
|
||||
for removal in role_removals:
|
||||
target_mention = removal.get('target_user_mention')
|
||||
role_name = removal.get('role_name')
|
||||
role = discord.utils.find(lambda r: r.name.casefold() == role_name.casefold(), message.guild.roles)
|
||||
if not role:
|
||||
await message.channel.send(f"Rôle '{role_name}' introuvable.")
|
||||
continue
|
||||
|
||||
# Check for bulk removal
|
||||
if target_mention.lower() in ['@everyone', 'everyone', 'all', 'tous']:
|
||||
total_members = 0
|
||||
removed_count = 0
|
||||
await message.channel.send(f"Retrait du rôle '{role.name}' de tous les membres... (Cela peut prendre du temps)")
|
||||
for member in message.guild.members:
|
||||
if member.bot:
|
||||
continue # Skip bots
|
||||
if role in member.roles:
|
||||
try:
|
||||
await member.remove_roles(role, reason=f"Rôle retiré par le Superviseur de tous les membres")
|
||||
removed_count += 1
|
||||
total_members += 1
|
||||
if removed_count % 10 == 0: # Progress update every 10 removes
|
||||
await message.channel.send(f"Progression: {removed_count} sur {total_members} membres...")
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Impossible de retirer le rôle de {member.mention} (permissions insuffisantes)")
|
||||
continue
|
||||
except Exception as e:
|
||||
await message.channel.send(f"Erreur lors du retrait à {member.mention}: {str(e)}")
|
||||
continue
|
||||
await message.channel.send(f"Rôle '{role.name}' retiré à {removed_count} membres (bots exclus).")
|
||||
continue
|
||||
|
||||
# Single user removal
|
||||
if target_mention.startswith('<@') and target_mention.endswith('>'):
|
||||
user_id = int(target_mention[2:-1])
|
||||
elif target_mention.startswith('<@!') and target_mention.endswith('>'):
|
||||
user_id = int(target_mention[3:-1])
|
||||
elif target_mention.startswith('@'):
|
||||
# Handle @username
|
||||
username = target_mention[1:] # Remove @
|
||||
member = discord.utils.find(lambda m: m.name == username or m.display_name == username, message.guild.members)
|
||||
if not member:
|
||||
await message.channel.send(f"Utilisateur {target_mention} introuvable.")
|
||||
continue
|
||||
user_id = member.id
|
||||
else:
|
||||
try:
|
||||
user_id = int(target_mention)
|
||||
except ValueError:
|
||||
await message.channel.send(f"Mention ou ID invalide: {target_mention}")
|
||||
continue
|
||||
member = message.guild.get_member(user_id)
|
||||
if not member:
|
||||
await message.channel.send(f"Utilisateur {target_mention} introuvable.")
|
||||
continue
|
||||
try:
|
||||
await member.remove_roles(role, reason=f"Rôle retiré par le Superviseur")
|
||||
removed_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour retirer le rôle {role.name} de {member.mention}.")
|
||||
if removed_count > 0 and not total_members:
|
||||
await message.channel.send(f"{removed_count} rôle(s) retiré(s).")
|
||||
23
commandes/roles/renommer.py
Normal file
23
commandes/roles/renommer.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
roles = params.get('roles', [])
|
||||
if not roles:
|
||||
# Fallback for single role
|
||||
roles = [params]
|
||||
guild = message.guild
|
||||
renamed_count = 0
|
||||
for role_params in roles:
|
||||
role_name = role_params.get('role_name')
|
||||
new_name = role_params.get('new_name')
|
||||
role = discord.utils.find(lambda r: r.name.casefold() == role_name.casefold(), guild.roles)
|
||||
if not role:
|
||||
await message.channel.send(f"Rôle '{role_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await role.edit(name=new_name)
|
||||
renamed_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour renommer le rôle '{role_name}'.")
|
||||
if renamed_count > 0:
|
||||
await message.channel.send(f"{renamed_count} rôle(s) renommé(s).")
|
||||
22
commandes/roles/supprimer.py
Normal file
22
commandes/roles/supprimer.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
roles = params.get('roles', [])
|
||||
if not roles:
|
||||
# Fallback for single role
|
||||
roles = [params]
|
||||
guild = message.guild
|
||||
deleted_count = 0
|
||||
for role_params in roles:
|
||||
role_name = role_params.get('role_name')
|
||||
role = discord.utils.find(lambda r: r.name.casefold() == role_name.casefold(), guild.roles)
|
||||
if not role:
|
||||
await message.channel.send(f"Rôle '{role_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await role.delete(reason="Supprimé par le Superviseur")
|
||||
deleted_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour supprimer le rôle '{role_name}'.")
|
||||
if deleted_count > 0:
|
||||
await message.channel.send(f"{deleted_count} rôle(s) supprimé(s).")
|
||||
BIN
commandes/salons/__pycache__/creer.cpython-310.pyc
Normal file
BIN
commandes/salons/__pycache__/creer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/creer.cpython-311.pyc
Normal file
BIN
commandes/salons/__pycache__/creer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/creer.cpython-312.pyc
Normal file
BIN
commandes/salons/__pycache__/creer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/deplacer.cpython-310.pyc
Normal file
BIN
commandes/salons/__pycache__/deplacer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/deplacer.cpython-311.pyc
Normal file
BIN
commandes/salons/__pycache__/deplacer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/deplacer.cpython-312.pyc
Normal file
BIN
commandes/salons/__pycache__/deplacer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/modifier.cpython-310.pyc
Normal file
BIN
commandes/salons/__pycache__/modifier.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/modifier.cpython-311.pyc
Normal file
BIN
commandes/salons/__pycache__/modifier.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/modifier.cpython-312.pyc
Normal file
BIN
commandes/salons/__pycache__/modifier.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/renommer.cpython-310.pyc
Normal file
BIN
commandes/salons/__pycache__/renommer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/renommer.cpython-311.pyc
Normal file
BIN
commandes/salons/__pycache__/renommer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/renommer.cpython-312.pyc
Normal file
BIN
commandes/salons/__pycache__/renommer.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/supprimer.cpython-310.pyc
Normal file
BIN
commandes/salons/__pycache__/supprimer.cpython-310.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/supprimer.cpython-311.pyc
Normal file
BIN
commandes/salons/__pycache__/supprimer.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/salons/__pycache__/supprimer.cpython-312.pyc
Normal file
BIN
commandes/salons/__pycache__/supprimer.cpython-312.pyc
Normal file
Binary file not shown.
42
commandes/salons/creer.py
Normal file
42
commandes/salons/creer.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channels = params.get('channels', [])
|
||||
if not channels:
|
||||
# Fallback for single channel
|
||||
channels = [params]
|
||||
guild = message.guild
|
||||
created_count = 0
|
||||
for channel_params in channels:
|
||||
channel_name = channel_params.get('channel_name')
|
||||
category_name = channel_params.get('category_name')
|
||||
channel_type = channel_params.get('channel_type', 'textuel')
|
||||
if category_name:
|
||||
# Recherche flexible : trouver une catégorie dont le nom contient le terme recherché
|
||||
category = discord.utils.find(
|
||||
lambda cat: category_name.casefold() in cat.name.casefold(),
|
||||
guild.categories
|
||||
)
|
||||
if not category:
|
||||
# Create the category if it doesn't exist
|
||||
try:
|
||||
category = await guild.create_category(category_name)
|
||||
await message.channel.send(f"Catégorie '{category_name}' créée.")
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour créer la catégorie '{category_name}'.")
|
||||
continue
|
||||
else:
|
||||
category = None
|
||||
try:
|
||||
if channel_type.casefold() == 'textuel':
|
||||
channel = await guild.create_text_channel(channel_name, category=category)
|
||||
elif channel_type.casefold() == 'vocal':
|
||||
channel = await guild.create_voice_channel(channel_name, category=category)
|
||||
else:
|
||||
await message.channel.send(f"Type de salon invalide pour '{channel_name}'. Utilisez 'textuel' ou 'vocal'.")
|
||||
continue
|
||||
created_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour créer le salon '{channel_name}'.")
|
||||
if created_count > 0:
|
||||
await message.channel.send(f"{created_count} salon(s) créé(s).")
|
||||
27
commandes/salons/deplacer.py
Normal file
27
commandes/salons/deplacer.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channels = params.get('channels', [])
|
||||
if not channels:
|
||||
# Fallback for single channel
|
||||
channels = [params]
|
||||
guild = message.guild
|
||||
moved_count = 0
|
||||
for channel_params in channels:
|
||||
channel_name = channel_params.get('channel_name')
|
||||
new_category_name = channel_params.get('new_category_name')
|
||||
channel = discord.utils.find(lambda c: c.name.casefold() == channel_name.casefold(), guild.channels)
|
||||
if not channel:
|
||||
await message.channel.send(f"Salon '{channel_name}' introuvable.")
|
||||
continue
|
||||
category = discord.utils.find(lambda cat: cat.name.casefold() == new_category_name.casefold(), guild.categories)
|
||||
if not category:
|
||||
await message.channel.send(f"Catégorie '{new_category_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await channel.edit(category=category)
|
||||
moved_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour déplacer le salon '{channel_name}'.")
|
||||
if moved_count > 0:
|
||||
await message.channel.send(f"{moved_count} salon(s) déplacé(s).")
|
||||
23
commandes/salons/modifier.py
Normal file
23
commandes/salons/modifier.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channels = params.get('channels', [])
|
||||
if not channels:
|
||||
# Fallback for single channel
|
||||
channels = [params]
|
||||
guild = message.guild
|
||||
modified_count = 0
|
||||
for channel_params in channels:
|
||||
channel_name = channel_params.get('channel_name')
|
||||
new_name = channel_params.get('new_name')
|
||||
channel = discord.utils.find(lambda c: c.name.casefold() == channel_name.casefold(), guild.channels)
|
||||
if not channel:
|
||||
await message.channel.send(f"Salon '{channel_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await channel.edit(name=new_name)
|
||||
modified_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour modifier le salon '{channel_name}'.")
|
||||
if modified_count > 0:
|
||||
await message.channel.send(f"{modified_count} salon(s) modifié(s).")
|
||||
27
commandes/salons/purge.py
Normal file
27
commandes/salons/purge.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
purges = params.get('purges', [])
|
||||
if not purges:
|
||||
await message.channel.send("Aucune purge spécifiée.")
|
||||
return
|
||||
|
||||
for purge in purges:
|
||||
channel_name = purge.get('channel_name')
|
||||
amount = purge.get('amount', 0)
|
||||
if not channel_name or amount <= 0 or amount > 1000:
|
||||
await message.channel.send(f"Purge invalide: canal '{channel_name}', quantité {amount} (max 1000).")
|
||||
continue
|
||||
|
||||
channel = discord.utils.find(lambda c: c.name == channel_name, message.guild.channels)
|
||||
if not channel or not isinstance(channel, discord.TextChannel):
|
||||
await message.channel.send(f"Canal '{channel_name}' introuvable ou n'est pas un canal texte.")
|
||||
continue
|
||||
|
||||
try:
|
||||
deleted = await channel.purge(limit=amount, reason=f"Purge par {message.author}")
|
||||
await message.channel.send(f"Supprimé {len(deleted)} message(s) dans {channel.mention}.")
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour purger dans {channel.mention}.")
|
||||
except Exception as e:
|
||||
await message.channel.send(f"Erreur lors de la purge dans {channel.mention}: {str(e)}")
|
||||
23
commandes/salons/renommer.py
Normal file
23
commandes/salons/renommer.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channels = params.get('channels', [])
|
||||
if not channels:
|
||||
# Fallback for single channel
|
||||
channels = [params]
|
||||
guild = message.guild
|
||||
renamed_count = 0
|
||||
for channel_params in channels:
|
||||
channel_name = channel_params.get('channel_name')
|
||||
new_name = channel_params.get('new_name')
|
||||
channel = discord.utils.find(lambda c: c.name.casefold() == channel_name.casefold(), guild.channels)
|
||||
if not channel:
|
||||
await message.channel.send(f"Salon '{channel_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await channel.edit(name=new_name)
|
||||
renamed_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour renommer le salon '{channel_name}'.")
|
||||
if renamed_count > 0:
|
||||
await message.channel.send(f"{renamed_count} salon(s) renommé(s).")
|
||||
22
commandes/salons/supprimer.py
Normal file
22
commandes/salons/supprimer.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import discord
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channels = params.get('channels', [])
|
||||
if not channels:
|
||||
# Fallback for single channel
|
||||
channels = [params]
|
||||
guild = message.guild
|
||||
deleted_count = 0
|
||||
for channel_params in channels:
|
||||
channel_name = channel_params.get('channel_name')
|
||||
channel = discord.utils.find(lambda c: c.name.casefold() == channel_name.casefold(), guild.channels)
|
||||
if not channel:
|
||||
await message.channel.send(f"Salon '{channel_name}' introuvable.")
|
||||
continue
|
||||
try:
|
||||
await channel.delete(reason="Supprimé par le Superviseur")
|
||||
deleted_count += 1
|
||||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour supprimer le salon '{channel_name}'.")
|
||||
if deleted_count > 0:
|
||||
await message.channel.send(f"{deleted_count} salon(s) supprimé(s).")
|
||||
BIN
commandes/security/__pycache__/auto_moderation.cpython-311.pyc
Normal file
BIN
commandes/security/__pycache__/auto_moderation.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/auto_moderation.cpython-312.pyc
Normal file
BIN
commandes/security/__pycache__/auto_moderation.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/ban.cpython-311.pyc
Normal file
BIN
commandes/security/__pycache__/ban.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/ban.cpython-312.pyc
Normal file
BIN
commandes/security/__pycache__/ban.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/kick.cpython-311.pyc
Normal file
BIN
commandes/security/__pycache__/kick.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/kick.cpython-312.pyc
Normal file
BIN
commandes/security/__pycache__/kick.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/mute.cpython-311.pyc
Normal file
BIN
commandes/security/__pycache__/mute.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/mute.cpython-312.pyc
Normal file
BIN
commandes/security/__pycache__/mute.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/purge.cpython-311.pyc
Normal file
BIN
commandes/security/__pycache__/purge.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/purge.cpython-312.pyc
Normal file
BIN
commandes/security/__pycache__/purge.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/timeout.cpython-311.pyc
Normal file
BIN
commandes/security/__pycache__/timeout.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/unban.cpython-311.pyc
Normal file
BIN
commandes/security/__pycache__/unban.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/unban.cpython-312.pyc
Normal file
BIN
commandes/security/__pycache__/unban.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/unmute.cpython-312.pyc
Normal file
BIN
commandes/security/__pycache__/unmute.cpython-312.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/warn.cpython-311.pyc
Normal file
BIN
commandes/security/__pycache__/warn.cpython-311.pyc
Normal file
Binary file not shown.
BIN
commandes/security/__pycache__/warn.cpython-312.pyc
Normal file
BIN
commandes/security/__pycache__/warn.cpython-312.pyc
Normal file
Binary file not shown.
198
commandes/security/auto_moderation.py
Normal file
198
commandes/security/auto_moderation.py
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
"""
|
||||
Module de modération automatique pour Superviseur Bot
|
||||
Gère la détection et sanction des messages inappropriés automatiquement sur tous les messages
|
||||
"""
|
||||
|
||||
import discord
|
||||
import aiohttp
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
WARNINGS_FILE = 'warnings.json'
|
||||
|
||||
def load_warnings():
|
||||
"""Charge les avertissements depuis le fichier JSON"""
|
||||
if os.path.exists(WARNINGS_FILE):
|
||||
with open(WARNINGS_FILE, 'r', encoding='utf-8') as f:
|
||||
content = f.read().strip()
|
||||
if content:
|
||||
return json.loads(content)
|
||||
return {}
|
||||
|
||||
def save_warnings(warnings):
|
||||
"""Sauvegarde les avertissements dans le fichier JSON"""
|
||||
with open(WARNINGS_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(warnings, f, indent=4, ensure_ascii=False)
|
||||
|
||||
|
||||
async def check_message_content_global(bot, content):
|
||||
"""
|
||||
Vérifie le contenu d'un message pour tout contenu inapproprié
|
||||
Utilise une liste locale de mots inappropriés pour performance optimale
|
||||
"""
|
||||
# Liste optimisée de mots inappropriés (mots-clés critiques seulement pour performance)
|
||||
bad_words = {
|
||||
# Mots critiques (haute sévérité)
|
||||
'nigger', 'nigga', 'pédé', 'bougnoul', 'viol', 'pédophile', 'inceste', 'naziste', 'hitler',
|
||||
'suicide', 'se tuer', 'se suicider', 'tuer', 'assassiner', 'meurtre',
|
||||
# Gros mots courants
|
||||
'fuck', 'shit', 'asshole', 'bitch', 'dick', 'pussy', 'cunt', 'motherfucker',
|
||||
'merde', 'putain', 'salope', 'connard', 'enculé', 'bordel', 'emmerde',
|
||||
'ta gueule', 'va te faire foutre', 'va te faire enculer', 'va chier', 'crève',
|
||||
'branleur', 'sucer', 'pipe', 'pétasse', 'garce', 'traînée', 'salopard', 'ordure',
|
||||
'fumier', 'pourri', 'dégueulasse', 'gueule', 'gueuler',
|
||||
# Insultes
|
||||
'con', 'abruti', 'débile', 'crétin', 'imbécile', 'loser', 'nullos', 'naze',
|
||||
'ridicule', 'pathétique', 'minable', 'nul', 'inutile', 'incapable', 'bon à rien',
|
||||
# NSFW/sexuel
|
||||
'sexe', 'porn', 'porno', 'bite', 'seins', 'sodomie', 'agression sexuelle',
|
||||
'harceleur', 'stalker',
|
||||
# Discrimination
|
||||
'raciste', 'antisémite', 'fasciste', 'gay', 'homo', 'féminin',
|
||||
# Drogues
|
||||
'drogue', 'cocaine', 'héroine', 'cannabis', 'ecstasy', 'lsd', 'crack', 'speed',
|
||||
'amphétamine', 'steroide', 'anabolisant',
|
||||
# Autres
|
||||
'cancer', 'maladie grave', 'crever', 'mourir'
|
||||
}
|
||||
|
||||
content_lower = content.lower()
|
||||
detected_bad_words = []
|
||||
|
||||
# Recherche rapide avec set pour performance
|
||||
for bad_word in bad_words:
|
||||
bad_word_lower = bad_word.lower()
|
||||
# Recherche insensible à la casse avec délimiteurs pour éviter faux positifs
|
||||
if f" {bad_word_lower} " in f" {content_lower} " or content_lower == bad_word_lower:
|
||||
detected_bad_words.append(bad_word)
|
||||
# Continue pour lister tous les mots détectés, mais limite à 3 pour performance
|
||||
if len(detected_bad_words) >= 3:
|
||||
break
|
||||
|
||||
if detected_bad_words:
|
||||
severity = "high" if any(word in ['nigger', 'nigga', 'pédé', 'bougnoul', 'viol', 'pédophile', 'inceste', 'naziste', 'hitler', 'suicide', 'se tuer', 'tuer', 'assassiner', 'meurtre'] for word in detected_bad_words) else "medium"
|
||||
return {
|
||||
"inappropriate": True,
|
||||
"severity": severity,
|
||||
"reason": f"Mots inappropriés détectés: {', '.join(detected_bad_words[:3])}",
|
||||
"bad_words": detected_bad_words[:3]
|
||||
}
|
||||
|
||||
return {"inappropriate": False}
|
||||
|
||||
|
||||
async def warn_and_check_ban_global(message, reason, message_deleted=False):
|
||||
"""
|
||||
Émet un avertissement et vérifie si l'utilisateur doit être banni (3+ avertissements)
|
||||
"""
|
||||
guild_id = str(message.guild.id)
|
||||
all_warnings = load_warnings()
|
||||
|
||||
if guild_id not in all_warnings:
|
||||
all_warnings[guild_id] = {}
|
||||
|
||||
user_id_str = str(message.author.id)
|
||||
if user_id_str not in all_warnings[guild_id]:
|
||||
all_warnings[guild_id][user_id_str] = []
|
||||
|
||||
# Ajouter l'avertissement
|
||||
all_warnings[guild_id][user_id_str].append({
|
||||
'reason': reason,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'issued_by': 'AUTOMATIC_MODERATION'
|
||||
})
|
||||
|
||||
warning_count = len(all_warnings[guild_id][user_id_str])
|
||||
save_warnings(all_warnings)
|
||||
|
||||
from commandes.autres.logger import action_logger
|
||||
action_logger.log_action('AUTOMATIC_MODERATION', str(message.author), str(message.guild), {
|
||||
'reason': reason,
|
||||
'warning_count': warning_count,
|
||||
'original_message': message.content
|
||||
}, True)
|
||||
|
||||
# Vérifier si bannissement automatique nécessaire
|
||||
if warning_count >= 3:
|
||||
try:
|
||||
await message.author.ban(reason=f"Bannissement automatique: 3 avertissements accumulés. Dernière raison: {reason}")
|
||||
embed = discord.Embed(
|
||||
title="🚫 Bannissement automatique",
|
||||
description=f"{message.author.mention}, vous avez été banni pour accumulation d'avertissements (3+ avertissements).\n**Dernière raison:** {reason}",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
embed.set_footer(text=f"Total d'avertissements: {warning_count}")
|
||||
await message.channel.send(embed=embed)
|
||||
return True # Utilisateur banni
|
||||
except discord.Forbidden:
|
||||
embed = discord.Embed(
|
||||
title="Ban requis",
|
||||
description=f"❌ {message.author.mention} a 3+ avertissements, mais le bot n'a pas les permissions de bannir.\n**Agissez manuellement!**",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
embed.set_footer(text=f"Total d'avertissements: {warning_count}")
|
||||
await message.channel.send(embed=embed)
|
||||
return False
|
||||
else:
|
||||
# Envoyer message d'avertissement
|
||||
deletion_text = "Votre message a été supprimé pour contenu inapproprié." if message_deleted else "Mots inappropriés détectés dans votre message."
|
||||
warning_message = f"⚠️ **Avertissement automatique** {message.author.mention}\n{deletion_text}\n**Raison:** {reason}\n**Avertissements totaux:** {warning_count}\n**Avant bannissement:** {3 - warning_count} avertissement(s)\n*Modération automatique par IA*"
|
||||
await message.channel.send(warning_message)
|
||||
return False
|
||||
|
||||
|
||||
async def moderate_message(bot, message):
|
||||
"""
|
||||
Fonction principale de modération automatique d'un message
|
||||
Retourne True si le message a été modéré (et donc supprimé), False sinon
|
||||
"""
|
||||
# Préparer le contenu à analyser
|
||||
content_to_check = message.content
|
||||
if message.attachments:
|
||||
for attachment in message.attachments:
|
||||
if attachment.content_type and attachment.content_type.startswith('image/'):
|
||||
if not content_to_check.strip():
|
||||
content_to_check = f"[Image: {attachment.filename}]"
|
||||
else:
|
||||
content_to_check += f" [Image: {attachment.filename}]"
|
||||
|
||||
# Analyser le contenu
|
||||
if content_to_check.strip():
|
||||
print(f"[MODERATION] Analysing message from {message.author}: '{content_to_check}'")
|
||||
moderation_result = await check_message_content_global(bot, content_to_check)
|
||||
print(f"[MODERATION] Result: {moderation_result}")
|
||||
|
||||
bad_words = moderation_result.get('bad_words', [])
|
||||
inappropriate = moderation_result.get('inappropriate', False)
|
||||
print(f"[MODERATION] bad_words: {bad_words}, inappropriate: {inappropriate}")
|
||||
|
||||
# Toujours donner un avertissement si des mots inappropriés sont détectés
|
||||
if bad_words:
|
||||
reason = f"Mots inappropriés détectés: {', '.join(bad_words[:5])}"
|
||||
print(f"[MODERATION] Processing bad words, reason: {reason}")
|
||||
if inappropriate:
|
||||
print(f"[MODERATION] Message inappropriate, deleting...")
|
||||
try:
|
||||
await message.delete()
|
||||
print(f"[MODERATION] Message deleted successfully")
|
||||
except discord.Forbidden:
|
||||
print(f"[MODERATION] Forbidden to delete message")
|
||||
pass # Impossible de supprimer
|
||||
except Exception as e:
|
||||
print(f"[MODERATION] Error deleting message: {e}")
|
||||
await warn_and_check_ban_global(message, reason, message_deleted=True)
|
||||
print(f"[MODERATION] Warning sent and ban checked, returning True")
|
||||
return True # Message modéré
|
||||
else:
|
||||
print(f"[MODERATION] Not inappropriate, sending warning without deletion")
|
||||
await warn_and_check_ban_global(message, reason, message_deleted=False)
|
||||
|
||||
return False # Message autorisé
|
||||
|
||||
|
||||
# Test function pour vérifier la fonctionnalité
|
||||
async def test_auto_moderation():
|
||||
"""Fonction de test pour vérifier que les avertissements sont toujours donnés"""
|
||||
print("Test auto-moderation: vérification que les avertissements sont donnés pour les mots inappropriés")
|
||||
# Cette fonction peut être appelée pour tester manuellement si nécessaire
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue