Beta/core/action_router.py

163 lines
7.4 KiB
Python
Raw Normal View History

import discord
import logging
from typing import Dict
logger = logging.getLogger('Beta')
class ActionExecutor:
"""Routes generated AI semantic actions to Discord administrative commands."""
def __init__(self, bot):
self.bot = bot
self._module_cache = {}
async def execute(self, action: str, params: Dict, message: discord.Message) -> (bool, str):
"""Execute an action from LLM response."""
# Normalize action name: replace spaces with underscores, uppercase
action = action.strip().upper().replace(' ', '_')
# Merge nested params if LLM created a sub-object by mistake
for k in ['parametre', 'parametres', 'params']:
if k in params and isinstance(params[k], dict):
params.update(params[k])
# --- INSIGHT HOOK (LLM generated task for Staff) ---
if action == 'INSIGHT':
content = params.get('message') or params.get('content') or params.get('insight') or "Pas de contenu"
if 'conflit' in content.lower() or 'tension' in content.lower():
logger.debug("Insight (Tension) logged.")
context_data = {
"author_mention": message.author.mention,
"author_name": message.author.display_name,
"channel_mention": message.channel.mention if hasattr(message.channel, 'mention') else 'DM',
"content": message.content[:200]
}
await self.bot.dispatcher.dispatch_insight(
insight=content,
importance=params.get('importance', 2),
guild=message.guild,
context_data=context_data
)
return True, None
# -----------------------------
# --- ALERT HOOK (LLM generated alert for Admin) ---
if action == 'ALERT':
content = params.get('message') or params.get('content') or params.get('alert') or "Alerte vide"
embed = discord.Embed(title="🚨 ALERTE DIRECTE (IA)", description=content, color=discord.Color.red())
embed.set_footer(text=f"Source: {message.author.display_name} | Salon: {message.channel}")
for admin_id in self.bot.admin_ids:
u = self.bot.get_user(admin_id) or await self.bot.fetch_user(admin_id)
if u: await u.send(embed=embed)
return True, None
# -----------------------------
# --- VOICE ACTIONS ---
if action == 'JOIN_VOICE':
voice_state = getattr(message.author, 'voice', None)
if not voice_state and self.bot.guild_id:
guild = self.bot.get_guild(self.bot.guild_id)
if guild:
member = guild.get_member(message.author.id) or await guild.fetch_member(message.author.id)
if member:
voice_state = member.voice
if voice_state and voice_state.channel:
await self.bot.voice_manager.join_channel(voice_state.channel)
return True, f"Connecté à {voice_state.channel.name}"
else:
return False, "Vous n'êtes pas dans un salon vocal identifiable par le système."
if action == 'LEAVE_VOICE':
await self.bot.voice_manager.leave_current()
return True, None
# --- PRIVACY ACTIONS ---
if action == 'FORGET_USER':
from brain.memory import delete_user_memory
guild_name = message.guild.name if message.guild else None
if guild_name:
from .utils import sanitize_guild_name
guild_name = sanitize_guild_name(guild_name)
delete_user_memory(message.author.id, guild_name)
return True, None
# --- PERMISSION ENFORCEMENT (ADMIN ONLY) ---
# Publicly accessible actions
PUBLIC_ACTIONS = {'PING', 'HELP', 'STATUS', 'JOIN_VOICE', 'LEAVE_VOICE', 'FORGET_USER'}
if action not in PUBLIC_ACTIONS:
is_admin_id = message.author.id in self.bot.admin_ids
has_admin_perms = message.author.guild_permissions.administrator if hasattr(message.author, 'guild_permissions') else False
if not (is_admin_id or has_admin_perms):
logger.warning(f"◈ PERMISSION DENIED: {message.author.display_name} a tenté l'action '{action}'")
return False, "Cette opération est réservée aux administrateurs."
action_map = {
'KICK': 'commandes.security.kick',
'BAN': 'commandes.security.ban',
'UNBAN': 'commandes.security.unban',
'UNMUTE': 'commandes.security.unmute',
'MUTE': 'commandes.security.mute',
'TIMEOUT': 'commandes.security.timeout',
'PURGE': 'commandes.security.purge',
'WARN': 'commandes.security.warn',
'LIST_WARNINGS': 'commandes.security.list_warnings',
'CREATE_CHANNEL': 'commandes.salons.creer',
'DELETE_CHANNEL': 'commandes.salons.supprimer',
'MODIFY_CHANNEL': 'commandes.salons.modifier',
'RENAME_CHANNEL': 'commandes.salons.renommer',
'MOVE_CHANNEL': 'commandes.salons.deplacer',
'CREATE_ROLE': 'commandes.roles.creer',
'DELETE_ROLE': 'commandes.roles.supprimer',
'MODIFY_ROLE': 'commandes.roles.modifier',
'RENAME_ROLE': 'commandes.roles.renommer',
'MOVE_ROLE': 'commandes.roles.deplacer',
'ADD_ROLE_TO_USER': 'commandes.roles.add_role',
'REMOVE_ROLE_FROM_USER': 'commandes.roles.remove_role',
'CREATE_CATEGORY': 'commandes.categories.creer',
'DELETE_CATEGORY': 'commandes.categories.supprimer',
'MODIFY_CATEGORY': 'commandes.categories.modifier',
'RENAME_CATEGORY': 'commandes.categories.renommer',
'MOVE_CATEGORY': 'commandes.categories.deplacer',
'PING': 'commandes.autres.ping',
'SET_WELCOME_CHANNEL': 'commandes.autres.setwelcomechannel',
'SET_GOODBYE_CHANNEL': 'commandes.autres.setgoodbyechannel',
'SET_MUTE_ROLE': 'commandes.autres.setmuterole',
'SEND_MESSAGE': 'commandes.autres.send_message',
'HELP': 'commandes.autres.help',
'STATUS': 'commandes.autres.status',
}
module_name = action_map.get(action)
if module_name:
logger.debug(f"Importing module: {module_name}")
try:
module = self._module_cache.get(module_name)
if module is None:
module = __import__(module_name, fromlist=['execute'])
self._module_cache[module_name] = module
logger.debug(f"Module {module_name} imported successfully")
await module.execute(self.bot, params, message)
logger.info(f"Action '{action}' executed successfully")
return True, None
except Exception as e:
error_msg = str(e)
logger.error(f"Error during action execution '{action}': {error_msg}")
return False, error_msg
else:
error_msg = f"Unknown action: {action}"
logger.error(error_msg)
return False, error_msg