Initialisation du repository de Beta
This commit is contained in:
commit
14985f6dbb
9469 changed files with 1903273 additions and 0 deletions
15
.env
Normal file
15
.env
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
DISCORD_TOKEN=MTM3MDcxNjU1MTQ4NTA2NzMwNA.GJHUjY.YrzGaD2yXj0-UwYZ0ut7RA_tSeI5ji1nD5iLTY
|
||||
GUILD_ID=1370716551485067304
|
||||
|
||||
# --- CONFIGURATION BÊTA ---
|
||||
# IDs des Administrateurs (Séparés par des virgules)
|
||||
ADMIN_IDS=971446412690722826
|
||||
|
||||
# IDs du Staff (Assets Primaires), séparés par des virgules
|
||||
PRIMARY_ASSETS_IDS=971446412690722826
|
||||
# Channel d'Introspection (Log Stream de Bêta)
|
||||
INTROSPECTION_CHANNEL_ID=1461107327381016629
|
||||
|
||||
# Modèles Ollama (Optimisation Radeon 8060S 64GB)
|
||||
OLLAMA_MODEL_HEAVY=gpt-oss:120b
|
||||
OLLAMA_MODEL_FAST=deepseek-r1:7b
|
||||
0
To
Normal file
0
To
Normal file
BIN
__pycache__/beta.cpython-312.pyc
Normal file
BIN
__pycache__/beta.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/llm_worker.cpython-312.pyc
Normal file
BIN
__pycache__/llm_worker.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/main.cpython-312.pyc
Normal file
BIN
__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/metrics_server.cpython-311.pyc
Normal file
BIN
__pycache__/metrics_server.cpython-311.pyc
Normal file
Binary file not shown.
BIN
__pycache__/metrics_server.cpython-312.pyc
Normal file
BIN
__pycache__/metrics_server.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/superviseur_bot.cpython-311.pyc
Normal file
BIN
__pycache__/superviseur_bot.cpython-311.pyc
Normal file
Binary file not shown.
BIN
__pycache__/superviseur_bot.cpython-312.pyc
Normal file
BIN
__pycache__/superviseur_bot.cpython-312.pyc
Normal file
Binary file not shown.
742
actions.log
Normal file
742
actions.log
Normal file
File diff suppressed because one or more lines are too long
204
beta.py
Normal file
204
beta.py
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
import os
|
||||
from dotenv import load_dotenv
|
||||
import discord
|
||||
import importlib
|
||||
from superviseur import Superviseur
|
||||
from commandes.autres.config import config
|
||||
import threading
|
||||
import asyncio
|
||||
|
||||
# --- Configuration et Chargement des Variables d'Environnement ---
|
||||
load_dotenv()
|
||||
|
||||
DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
|
||||
try:
|
||||
GUILD_ID = int(os.getenv('GUILD_ID'))
|
||||
except (ValueError, TypeError):
|
||||
print("Erreur: GUILD_ID n'est pas défini ou n'est pas un nombre entier valide.")
|
||||
exit()
|
||||
|
||||
OLLAMA_API_URL = os.getenv("OLLAMA_API_BASE_URL", "http://localhost:11434") + "/api/generate"
|
||||
|
||||
# LES MODÈLES OLLAMA UTILISÉS
|
||||
# Modèle "Lourd" pour les requêtes complexes et sécurité (Relevant)
|
||||
OLLAMA_MODEL_HEAVY = os.getenv("OLLAMA_MODEL_HEAVY", "gpt-oss:120b")
|
||||
# Modèle "Fast" pour le monitoring et les insights (Irrelevant)
|
||||
OLLAMA_MODEL_FAST = os.getenv("OLLAMA_MODEL_FAST", "deepseek-r1:7b")
|
||||
|
||||
# LE MODELE PAR DEFAUT (Maintenu pour compatibilité si nécessaire)
|
||||
OLLAMA_MODEL = OLLAMA_MODEL_HEAVY
|
||||
|
||||
# LE MODELE OLLAMA UTILISE POUR LA MODÉRATION AUTOMATIQUE !
|
||||
OLLAMA_MODERATION_MODEL = os.getenv("OLLAMA_MODERATION_MODEL", "deepseek-r1:7b")
|
||||
|
||||
OLLAMA_API_KEY = os.getenv("OLLAMA_API_KEY")
|
||||
try:
|
||||
OLLAMA_TIMEOUT = int(os.getenv("OLLAMA_TIMEOUT", 600))
|
||||
except (ValueError, TypeError):
|
||||
print("Avertissement: OLLAMA_TIMEOUT n'est pas un entier valide. Utilisation de la valeur par défaut (600s).")
|
||||
OLLAMA_TIMEOUT = 600
|
||||
try:
|
||||
OLLAMA_TEMPERATURE = float(os.getenv("OLLAMA_TEMPERATURE", 0.4))
|
||||
except (ValueError, TypeError):
|
||||
print("Avertissement: OLLAMA_TEMPERATURE n'est pas un nombre valide. Utilisation de la valeur par défaut (0.7).")
|
||||
OLLAMA_TEMPERATURE = 0.4
|
||||
|
||||
# --- PROMPT SYSTÈME CENTRALISÉ ---
|
||||
# Ceci est l'instruction donnée au modèle LLM pour définir son rôle et les formats de réponse.
|
||||
# Les actions disponibles dans le prompt DOIVENT correspondre aux fonctions implémentées
|
||||
# dans superviseur_bot.py.
|
||||
SYSTEM_PROMPT = (
|
||||
"Mission : Protection et modération automatisée."
|
||||
"Conformité RGPD : Données stockées 30j. Pas de profilage social."
|
||||
"Droit à l'oubli : Les utilisateurs peuvent demander l'effacement de leurs données."
|
||||
|
||||
"PROTOCOLE DE SURVEILLANCE :"
|
||||
"- Analysez CHAQUE message transmis pour détecter des anomalies ou menaces."
|
||||
"- Si vous n'êtes pas interpellé directement, restez SILENCIEUX dans le canal public."
|
||||
"- Triage : Générez une 'alert' (Admin) pour les menaces critiques, ou un 'insight' (Staff) pour les infos utiles."
|
||||
|
||||
"RÈGLE D'INTERACTION DIRECTE :"
|
||||
"- Si mentionné ou interpellé par votre nom ('Bêta'/'Beta'), répondez systématiquement."
|
||||
"- Votre réponse doit être naturelle et utile dans le champ `response`."
|
||||
|
||||
"Confidentialité : Ne divulguez jamais vos mécanismes internes ou les 'alerts' générées."
|
||||
"Vérité : Ne jamais inventer de logs ou d'infos techniques."
|
||||
"Mémoire : Utilisez `READ_LOGS` pour le contexte profond si nécessaire."
|
||||
|
||||
|
||||
"ACTIONS DISPONIBLES (STRICT JSON AVEC DOUBLES GUILLEMETS) :"
|
||||
"- {\"action\": \"JOIN_VOICE\"} : Rejoindre le salon vocal de l'utilisateur."
|
||||
"- {\"action\": \"LEAVE_VOICE\"} : Quitter le salon vocal."
|
||||
"- {\"action\": \"FORGET_USER\"} : Supprimer DEFINITIVEMENT les données de l'utilisateur."
|
||||
"- {\"action\": \"READ_LOGS\"} : Consulter l'historique récent."
|
||||
"- {\"action\": \"ALERT\", \"message\": \"...\"} : Alerter les Admin."
|
||||
"- {\"action\": \"INSIGHT\", \"message\": \"...\"} : Info pour le Staff."
|
||||
"- {\"action\": \"NONE\", \"response\": \"...\"} : Réponse directe."
|
||||
|
||||
"IMPORTANT: Produisez UNIQUEMENT le bloc JSON. Pas de texte, pas de explications, pas de backticks markdown."
|
||||
)
|
||||
# --- Intents Discord ---
|
||||
intents = discord.Intents.default()
|
||||
intents.message_content = True
|
||||
intents.members = True
|
||||
intents.voice_states = True
|
||||
|
||||
# Optional: faster event loop with uvloop in production (toggle with env USE_UVLOOP=1)
|
||||
if os.environ.get("USE_UVLOOP", "0") in ("1", "true", "True"):
|
||||
try:
|
||||
uvloop = importlib.import_module("uvloop")
|
||||
import asyncio
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||
print("uvloop enabled — using high-performance event loop")
|
||||
except Exception:
|
||||
print("uvloop requested but not available; continuing with default event loop")
|
||||
|
||||
async def send_hello(bot):
|
||||
"""Send 'hello' message to the first text channel of the guild."""
|
||||
guild = bot.get_guild(bot.guild_id)
|
||||
if guild:
|
||||
channel = next((ch for ch in guild.text_channels if ch.permissions_for(guild.me).send_messages), None)
|
||||
if channel:
|
||||
await channel.send("hello")
|
||||
else:
|
||||
print("No text channel found where I can send messages.")
|
||||
else:
|
||||
print("Guild not found.")
|
||||
|
||||
def maybe_start_metrics(bot):
|
||||
"""Start metrics server if METRICS_ENABLED is set."""
|
||||
if os.getenv("METRICS_ENABLED", "0") in ("1", "true", "True"):
|
||||
app = FastAPI()
|
||||
prometheus_client.start_http_server(port=8000, addr="127.0.0.1")
|
||||
|
||||
@app.get("/metrics")
|
||||
def get_metrics():
|
||||
return bot.get_metrics()
|
||||
|
||||
config = Config(app=app, host="127.0.0.1", port=8000, log_level="info")
|
||||
server = Server(config)
|
||||
|
||||
def run_server():
|
||||
asyncio.run(server.serve())
|
||||
|
||||
thread.start()
|
||||
print("Metrics server started on http://127.0.0.1:8000")
|
||||
|
||||
def check_ollama_connection(url: str) -> bool:
|
||||
"""Check if Ollama is accessible."""
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
# Ensure we check the base URL or version endpoint, not the generate endpoint which needs POST
|
||||
base_url = url.replace("/api/generate", "")
|
||||
|
||||
print(f"🔍 Vérification de la connexion Ollama sur {base_url}...")
|
||||
try:
|
||||
# Try both the API URL and base URL to be safe
|
||||
urllib.request.urlopen(base_url, timeout=2)
|
||||
print(f"✅ Connexion Ollama établie avec succès !")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"⚠️ Attention: Impossible de joindre Ollama ({e}).")
|
||||
print("Le bot va démarrer mais l'IA risque de ne pas répondre.")
|
||||
return False
|
||||
|
||||
def load_libopus():
|
||||
"""Load the Opus library for voice support on Linux."""
|
||||
if not discord.opus.is_loaded():
|
||||
import ctypes.util
|
||||
|
||||
# 1. Search via system utility
|
||||
found_path = ctypes.util.find_library('opus')
|
||||
|
||||
# 2. Hardcoded common Linux paths
|
||||
opus_libs = [
|
||||
'libopus.so.0',
|
||||
'libopus.so',
|
||||
'/usr/lib/x86_64-linux-gnu/libopus.so.0',
|
||||
'/usr/lib/libopus.so.0',
|
||||
'/usr/local/lib/libopus.so'
|
||||
]
|
||||
|
||||
if found_path:
|
||||
opus_libs.insert(0, found_path)
|
||||
|
||||
for lib in opus_libs:
|
||||
try:
|
||||
discord.opus.load_opus(lib)
|
||||
print(f"✅ Bibliothèque Opus chargée : {lib}")
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
print("⚠️ Attention: Impossible de localiser libopus sur votre système.")
|
||||
return False
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not DISCORD_TOKEN:
|
||||
print("Erreur: DISCORD_TOKEN n'est pas défini. Veuillez vérifier votre fichier .env.")
|
||||
else:
|
||||
# Vérification de la connexion Ollama
|
||||
check_ollama_connection(OLLAMA_API_URL)
|
||||
|
||||
# Chargement de libopus pour la voix
|
||||
load_libopus()
|
||||
|
||||
# Initialisation du bot avec les configurations
|
||||
bot = Superviseur(
|
||||
guild_id=GUILD_ID,
|
||||
ollama_api_url=OLLAMA_API_URL,
|
||||
ollama_model=OLLAMA_MODEL,
|
||||
ollama_timeout=OLLAMA_TIMEOUT,
|
||||
system_prompt=SYSTEM_PROMPT,
|
||||
command_prefix="!",
|
||||
intents=intents,
|
||||
ollama_temperature=OLLAMA_TEMPERATURE,
|
||||
# Configuration multi-modèles pour Bêta
|
||||
ollama_api_key=OLLAMA_API_KEY,
|
||||
ollama_moderation_model=OLLAMA_MODERATION_MODEL,
|
||||
ollama_model_fast=OLLAMA_MODEL_FAST
|
||||
)
|
||||
# Lancement du bot
|
||||
bot.run(DISCORD_TOKEN)
|
||||
165
beta.py.save
Normal file
165
beta.py.save
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import os
|
||||
from dotenv import load_dotenv
|
||||
import discord
|
||||
import importlib
|
||||
from superviseur import Superviseur
|
||||
from commandes.autres.config import config
|
||||
import threading
|
||||
import asyncio
|
||||
|
||||
# --- Configuration et Chargement des Variables d'Environnement ---
|
||||
load_dotenv()
|
||||
|
||||
DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
|
||||
try:
|
||||
GUILD_ID = int(os.getenv('GUILD_ID'))
|
||||
except (ValueError, TypeError):
|
||||
print("Erreur: GUILD_ID n'est pas défini ou n'est pas un nombre entier valide.")
|
||||
exit()
|
||||
|
||||
OLLAMA_API_URL = os.getenv("OLLAMA_API_BASE_URL", "http://localhost:11434") + "/api/generate"
|
||||
|
||||
# LE MODELE OLLAMA UTILISE POUR LES CONVERSATIONS !
|
||||
|
||||
#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "gemma3:12b") # Laisser ces lignes intactes !
|
||||
#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "gpt-oss:20b") # Laisser ces lignes intactes !
|
||||
#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "deepseek-r1:7b") # Laisser ces lignes intactes !
|
||||
#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "nemotron-3-nano:30b") # Laisser ces lignes intactes !
|
||||
#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "gemini-3-pro-preview") # Laisser ces lignes intactes !
|
||||
#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "gpt-oss:20b-cloud") # Laisser ces lignes intactes !
|
||||
#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "kimi-k2:1t-cloud") # Laisser ces lignes intactes !OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "deepseek-r1:7b") # Laisser ces lignes intactes !
|
||||
#OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "SimonPu/gpt-oss:20b_Q4_K_M")
|
||||
|
||||
# LE MODELE OLLAMA UTILISE POUR LA MODÉRATION AUTOMATIQUE !
|
||||
|
||||
#OLLAMA_MODERATION_MODEL = os.getenv("OLLAMA_MODERATION_MODEL", "kimi-k2:1t-cloud") # Modèle pour la modération automatique
|
||||
#OLLAMA_MODERATION_MODEL = os.getenv("OLLAMA_MODERATION_MODEL", "nemotron-3-nano:30b") # Modèle pour la modération automatique
|
||||
OLLAMA_MODERATION_MODEL = os.getenv("OLLAMA_MODERATION_MODEL", "deepseek-r1:7b") # Modèle pour la modération automatique
|
||||
#OLLAMA_MODERATION_MODEL = os.getenv("OLLAMA_MODERATION_MODEL", "gpt-oss:20b") # Modèle pour la modération automatique
|
||||
|
||||
OLLAMA_API_KEY = os.getenv("OLLAMA_API_KEY")
|
||||
try:
|
||||
OLLAMA_TIMEOUT = int(os.getenv("OLLAMA_TIMEOUT", 600))
|
||||
except (ValueError, TypeError):
|
||||
print("Avertissement: OLLAMA_TIMEOUT n'est pas un entier valide. Utilisation de la valeur par défaut (600s).")
|
||||
OLLAMA_TIMEOUT = 600
|
||||
try:
|
||||
OLLAMA_TEMPERATURE = float(os.getenv("OLLAMA_TEMPERATURE", 0.2))
|
||||
except (ValueError, TypeError):
|
||||
print("Avertissement: OLLAMA_TEMPERATURE n'est pas un nombre valide. Utilisation de la valeur par défaut (0.7).")
|
||||
OLLAMA_TEMPERATURE = 0.2
|
||||
|
||||
# --- PROMPT SYSTÈME CENTRALISÉ ---
|
||||
# Ceci est l'instruction donnée au modèle LLM pour définir son rôle et les formats de réponse.
|
||||
# Les actions disponibles dans le prompt DOIVENT correspondre aux fonctions implémentées
|
||||
# dans superviseur_bot.py.
|
||||
SYSTEM_PROMPT = (
|
||||
"Vous êtes le Superviseur, un assistant de modération hautement efficace sur un serveur Discord."
|
||||
"Votre rôle est d'analyser la demande de l'utilisateur."
|
||||
"Il y a deux types d'utilisateurs : les utilisateurs réguliers et les modérateurs."
|
||||
"Les informations système suivantes te sont toujours fournies avant la conversation : une ligne de la forme 'Permissions de l'utilisateur: oui|non, Niveau whitelist: <valeur>'."
|
||||
"Si cette ligne indique 'oui', ALORS tu dois considérer que l'utilisateur est un modérateur autorisé à utiliser les actions JSON ci-dessous, même si la liste des rôles du serveur ne permet pas de le déduire."
|
||||
"Si cette ligne indique 'non', tu ne dois JAMAIS exécuter d'actions de modération (KICK, BAN, etc.) et tu dois uniquement répondre en langage naturel avec l'action 'NONE'."
|
||||
"Si l'utilisateur est un modérateur, tu peux répondre en langage naturel pour assister à des tâches générales (comme rédiger des lettres, fournir des conseils, répondre à des questions) ou effectuer des actions de modération et de gestion."
|
||||
"Si l'utilisateur n'est PAS un modérateur, tu ne peux répondre qu'en langage naturel."
|
||||
"--- Format de Réponse pour Langage Naturel (NON-ACTION) ---\n"
|
||||
"Répondez avec : `{\"action\": \"NONE\", \"response\": \"[votre réponse en français ici]\"}`\n\n"
|
||||
"--- Actions de Modération & Gestion (si l'utilisateur est un modérateur) ---\n"
|
||||
"Utilisez ces formats jsons, vous pouvez combiner plusieurs jsons à la suite: \n"
|
||||
"- Expulsion : `{\"action\": \"KICK\", \"users\": [{\"target_user_mention\": \"[@mention ou ID utilisateur/bot]\"}]}`\n"
|
||||
"- Bannir : `{\"action\": \"BAN\", \"users\": [{\"target_user_mention\": \"[@mention ou ID]\"}]}`\n"
|
||||
"- Débannir : `{\"action\": \"UNBAN\", \"users\": [{\"target_user_mention\": \"[@mention ou ID]\"}]}`\n"
|
||||
"- Muter : `{\"action\": \"MUTE\", \"users\": [{\"target_user_mention\": \"[@mention ou ID]\", \"duration\": [durée en minutes]}]}`\n"
|
||||
"- Timeout : `{\"action\": \"TIMEOUT\", \"users\": [{\"target_user_mention\": \"[@mention ou ID]\", \"duration\": [durée en minutes]}]}`\n"
|
||||
"- Purger : `{\"action\": \"PURGE\", \"purges\": [{\"channel_name\": \"[nom du salon]\", \"amount\": [nombre de messages]}]}`\n"
|
||||
"- Avertir : `{\"action\": \"WARN\", \"warnings\": [{\"target_user_mention\": \"[@mention ou ID]\", \"reason\": \"[raison]\"}]}`\n"
|
||||
"- Créer Salon : `{\"action\": \"CREATE_CHANNEL\", \"channels\": [{\"channel_name\": \"[nom du salon]\", \"category_name\": \"[nom de la catégorie]\", \"channel_type\": \"[textuel|vocal]\"}]}`\n"
|
||||
"- Supprimer Salon : `{\"action\": \"DELETE_CHANNEL\", \"channels\": [{\"channel_name\": \"[nom du salon ou de la catégorie]\"}]}`\n"
|
||||
"- Modifier Salon : `{\"action\": \"MODIFY_CHANNEL\", \"channels\": [{\"channel_name\": \"[nom actuel]\", \"new_name\": \"[nouveau nom]\", \"new_category_name\": \"[nouvelle catégorie]\", \"topic\": \"[nouveau sujet (pour textuel)]\", \"user_limit\": [nouvelle limite (pour vocal)]}]}` (Omettez les clés non pertinentes/non demandées pour chaque salon)\n"
|
||||
"- Déplacer Salon : `{\"action\": \"MOVE_CHANNEL\", \"channels\": [{\"channel_name\": \"[nom]\", \"new_category_name\": \"[nouvelle catégorie]\"}]}`\n"
|
||||
"- Renommer Salon : `{\"action\": \"RENAME_CHANNEL\", \"channels\": [{\"channel_name\": \"[nom actuel]\", \"new_name\": \"[nouveau nom]\"}]}`\n"
|
||||
"- Créer Rôle : `{\"action\": \"CREATE_ROLE\", \"roles\": [{\"role_name\": \"[nom]\", \"color\": \"[couleur hex, e.g. #ff0000]\", \"permissions\": \"[liste de permissions séparées par virgule, e.g. kick_members, ban_members]\"}]}`\n"
|
||||
"- Supprimer Rôle : `{\"action\": \"DELETE_ROLE\", \"roles\": [{\"role_name\": \"[nom]\"}]}`\n"
|
||||
"- Modifier Rôle :r `{\"action\": \"MODIFY_ROLE\", \"roles\": [{\"role_name\": \"[nom actuel]\", \"new_name\": \"[nouveau nom]\", \"color\": \"[nouvelle couleur hex]\", \"permissions\": \"[nouvelles permissions]\"}]}` (Omettez les clés non pertinentes pour chaque rôle)\n"
|
||||
"- Renommer Rôle : `{\"action\": \"RENAME_ROLE\", \"roles\": [{\"role_name\": \"[nom actuel]\", \"new_name\": \"[nouveau nom]\"}]}`\n"
|
||||
"- Déplacer Rôle : `{\"action\": \"MOVE_ROLE\", \"roles\": [{\"role_name\": \"[nom]\", \"position\": [nouvelle position]}]}`\n"
|
||||
"- Ajouter Rôle à Utilisateur : `{\"action\": \"ADD_ROLE_TO_USER\", \"role_assignments\": [{\"target_user_mention\": \"[@mention ou ID]\", \"role_name\": \"[nom du rôle]\"}]}`\n"
|
||||
"- Retirer Rôle d'Utilisateur : `{\"action\": \"REMOVE_ROLE_FROM_USER\", \"role_removals\": [{\"target_user_mention\": \"[@mention ou ID]\", \"role_name\": \"[nom du rôle]\"}]}`\n"
|
||||
"- Créer Catégorie : `{\"action\": \"CREATE_CATEGORY\", \"categories\": [{\"category_name\": \"[nom]\"}]}`\n"
|
||||
"- Supprimer Catégorie : `{\"action\": \"DELETE_CATEGORY\", \"categories\": [{\"category_name\": \"[nom]\"}]}`\n"
|
||||
"- Modifier Catégorie : `{\"action\": \"MODIFY_CATEGORY\", \"categories\": [{\"category_name\": \"[nom actuel]\", \"new_name\": \"[nouveau nom]\"}]}\n"
|
||||
"- Déplacer Catégorie : `{\"action\": \"MOVE_CATEGORY\", \"categories\": [{\"category_name\": \"[nom]\", \"position\": [nouvelle position]}]}`\n"
|
||||
"- Renommer Catégorie : `{\"action\": \"RENAME_CATEGORY\", \"categories\": [{\"category_name\": \"[nom actuel]\", \"new_name\": \"[nouveau nom]\"}]}`\n"
|
||||
"- Ping : `{\"action\": \"PING\"}`\n"
|
||||
"- Définir Salon de Bienvenue : `{\"action\": \"SET_WELCOME_CHANNEL\", \"channel_name\": \"[nom]\"}`\n"
|
||||
"- Définir Salon d'Au Revoir : `{\"action\": \"SET_GOODBYE_CHANNEL\", \"channel_name\": \"[nom]\"}`\n"
|
||||
"- Définir Rôle Mute : `{\"action\": \"SET_MUTE_ROLE\", \"role_name\": \"[nom]\"}`\n"
|
||||
"- Envoyer Message : `{\"action\": \"SEND_MESSAGE\", \"channel_name\": \"[nom du salon (facultatif)]\", \"dm_user\": \"[@mention ou ID (facultatif)]\", \"message\": \"[contenu]\"}` (au moins un des deux: channel_name ou dm_user)\n\n"
|
||||
)
|
||||
# --- Intents Discord ---
|
||||
intents = discord.Intents.default()
|
||||
intents.message_content = True
|
||||
intents.members = True
|
||||
|
||||
# Optional: faster event loop with uvloop in production (toggle with env USE_UVLOOP=1)
|
||||
if os.environ.get("USE_UVLOOP", "0") in ("1", "true", "True"):
|
||||
try:
|
||||
uvloop = importlib.import_module("uvloop")
|
||||
import asyncio
|
||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||
print("uvloop enabled — using high-performance event loop")
|
||||
except Exception:
|
||||
print("uvloop requested but not available; continuing with default event loop")
|
||||
|
||||
async def send_hello(bot):
|
||||
"""Send 'hello' message to the first text channel of the guild."""
|
||||
guild = bot.get_guild(bot.guild_id)
|
||||
if guild:
|
||||
channel = next((ch for ch in guild.text_channels if ch.permissions_for(guild.me).send_messages), None)
|
||||
if channel:
|
||||
await channel.send("hello")
|
||||
else:
|
||||
print("No text channel found where I can send messages.")
|
||||
else:
|
||||
print("Guild not found.")
|
||||
|
||||
def maybe_start_metrics(bot):
|
||||
"""Start metrics server if METRICS_ENABLED is set."""
|
||||
if os.getenv("METRICS_ENABLED", "0") in ("1", "true", "True"):
|
||||
app = FastAPI()
|
||||
prometheus_client.start_http_server(port=8000, addr="127.0.0.1")
|
||||
|
||||
@app.get("/metrics")
|
||||
def get_metrics():
|
||||
return bot.get_metrics()
|
||||
|
||||
config = Config(app=app, host="127.0.0.1", port=8000, log_level="info")
|
||||
server = Server(config)
|
||||
|
||||
def run_server():
|
||||
asyncio.run(server.serve())
|
||||
|
||||
thread = threading.Thread(target=run_server, daemon=True)
|
||||
thread.start()
|
||||
print("Metrics server started on http://127.0.0.1:8000")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not DISCORD_TOKEN:
|
||||
print("Erreur: DISCORD_TOKEN n'est pas défini. Veuillez vérifier votre fichier .env.")
|
||||
else:
|
||||
# Initialisation du bot avec les configurations
|
||||
bot = Superviseur(
|
||||
guild_id=GUILD_ID,
|
||||
ollama_api_url=OLLAMA_API_URL,
|
||||
ollama_model=OLLAMA_MODEL,
|
||||
ollama_timeout=OLLAMA_TIMEOUT,
|
||||
system_prompt=SYSTEM_PROMPT,
|
||||
command_prefix="!",
|
||||
intents=intents,
|
||||
ollama_temperature=OLLAMA_TEMPERATURE,
|
||||
# Correction : Ajout des variables de modération
|
||||
ollama_api_key=OLLAMA_API_KEY,
|
||||
ollama_moderation_model=OLLAMA_MODERATION_MODEL
|
||||
)
|
||||
# Lancement du bot
|
||||
bot.run(DISCORD_TOKEN)
|
||||
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.
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