781 lines
40 KiB
PHP
Executable File
781 lines
40 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Discord Bot Daemon - Versión corregida
|
|
* Ejecutar con Supervisor: php bot_daemon_fixed.php
|
|
*/
|
|
|
|
use Discord\Discord;
|
|
use Discord\Parts\Channel\Message;
|
|
use Discord\Parts\Interactions\Interaction;
|
|
use Discord\Builders\MessageBuilder;
|
|
use Discord\Parts\Embed\Embed;
|
|
use Discord\Parts\Guild\Guild;
|
|
use Discord\WebSockets\Event;
|
|
use Discord\Builders\Components\ActionRow;
|
|
use Discord\Builders\Components\Button;
|
|
|
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
|
|
// --- Cargar variables de entorno ---
|
|
if (file_exists(__DIR__ . '/.env')) {
|
|
$lines = file(__DIR__ . '/.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if ($line === '' || strpos($line, '#') === 0) continue;
|
|
if (strpos($line, '=') === false) continue;
|
|
list($key, $value) = explode('=', $line, 2);
|
|
$k = trim($key);
|
|
$v = trim($value);
|
|
if ($k !== '') $_ENV[$k] = $v;
|
|
}
|
|
}
|
|
|
|
// Configurar el directorio de logs si no existe
|
|
if (!file_exists(__DIR__ . '/logs')) {
|
|
mkdir(__DIR__ . '/logs', 0777, true);
|
|
}
|
|
|
|
// Función para registrar interacciones y eventos en el log (GLOBAL)
|
|
$logInteraction = function($message, $data = []) {
|
|
$logMessage = date('[Y-m-d H:i:s] ') . $message . "\n";
|
|
if (!empty($data)) {
|
|
$logMessage .= "Datos: " . json_encode($data, JSON_PRETTY_PRINT) . "\n";
|
|
}
|
|
error_log($logMessage, 3, __DIR__ . '/logs/interaction_events.log');
|
|
};
|
|
|
|
|
|
// --- Configuración de opciones para DiscordPHP ---
|
|
$options = [
|
|
'token' => $_ENV['DISCORD_BOT_TOKEN'] ?? '',
|
|
|
|
'useTransportCompression' => false,
|
|
'intents' => \Discord\WebSockets\Intents::getDefaultIntents() |
|
|
\Discord\WebSockets\Intents::GUILD_MESSAGES |
|
|
\Discord\WebSockets\Intents::DIRECT_MESSAGES |
|
|
\Discord\WebSockets\Intents::MESSAGE_CONTENT |
|
|
\Discord\WebSockets\Intents::GUILD_MEMBERS,
|
|
'disabledEvents' => [
|
|
'TYPING_START',
|
|
'USER_SETTINGS_UPDATE',
|
|
'PRESENCE_UPDATE',
|
|
'VOICE_STATE_UPDATE'
|
|
],
|
|
// Deshabilitar el logger para evitar problemas
|
|
'logger' => null
|
|
];
|
|
|
|
// --- Inicializar el bot ---
|
|
$discord = new Discord($options);
|
|
|
|
// Variable global para la conexión a la base de datos
|
|
$db = null;
|
|
|
|
try {
|
|
$db = getDBConnection();
|
|
} catch (\Throwable $e) {
|
|
echo "No se pudo conectar a la base de datos: " . $e->getMessage() . PHP_EOL;
|
|
exit(1);
|
|
}
|
|
|
|
// --- Manejador de mensajes ---
|
|
$discord->on('MESSAGE_CREATE', function (Message $message, Discord $discord) use ($db, $logInteraction) {
|
|
try {
|
|
// Ignorar mensajes del propio bot
|
|
if ($message->author->bot) {
|
|
return;
|
|
}
|
|
|
|
// Registrar el mensaje recibido
|
|
$logMessage = sprintf("[%s] Mensaje recibido - ID: %s, Autor: %s, Canal: %s, Contenido: %s\n",
|
|
date('Y-m-d H:i:s'),
|
|
$message->id,
|
|
$message->author->username,
|
|
$message->channel->name,
|
|
substr($message->content, 0, 100) . (strlen($message->content) > 100 ? '...' : '')
|
|
);
|
|
error_log($logMessage, 3, __DIR__ . '/logs/message_events.log');
|
|
|
|
// Verificar si el mensaje necesita traducción
|
|
$needsTrans = needsTranslation($message->content);
|
|
error_log("¿Necesita traducción? " . ($needsTrans ? 'Sí' : 'No') . "\n", 3, __DIR__ . '/logs/message_events.log');
|
|
|
|
if ($needsTrans) {
|
|
try {
|
|
// Crear un botón de traducción con icono y un label de espacio en blanco
|
|
$button = new Button(Button::STYLE_PRIMARY);
|
|
$button->setCustomId("translate_{$message->id}") // customId solo con message.id
|
|
->setLabel("\u{200b}") // Espacio en blanco para que el botón sea válido
|
|
->setEmoji('🌍');
|
|
|
|
// Crear una fila de acción con el botón
|
|
$actionRow = new ActionRow();
|
|
$actionRow->addComponent($button);
|
|
|
|
// Crear el mensaje de respuesta (sin texto, solo el botón)
|
|
$replyMessage = MessageBuilder::new()
|
|
->addComponent($actionRow);
|
|
|
|
// Enviar el mensaje con el botón (visible para todos)
|
|
$message->channel->sendMessage($replyMessage)
|
|
->then(
|
|
function() use ($message) {
|
|
error_log("Botón de traducción enviado para el mensaje: {$message->id} (visible para todos)\n", 3, __DIR__ . '/logs/message_events.log');
|
|
},
|
|
function($error) {
|
|
error_log("Error al enviar botón (visible para todos): " . $error->getMessage() . "\n", 3, __DIR__ . '/logs/translation_errors.log');
|
|
}
|
|
);
|
|
|
|
} catch (\Throwable $e) {
|
|
error_log("Error en el manejador de mensajes: " . $e->getMessage() . "\n", 3, __DIR__ . '/logs/translation_errors.log');
|
|
}
|
|
}
|
|
} catch (\Throwable $e) {
|
|
error_log("Error en el manejador de mensajes (nivel superior): " . $e->getMessage() . "\n", 3, __DIR__ . '/logs/translation_errors.log');
|
|
}
|
|
});
|
|
|
|
|
|
// --- Manejador para nuevos miembros ---
|
|
$discord->on('GUILD_MEMBER_ADD', function (\Discord\Parts\User\Member $member, Discord $discord) use ($db, $logInteraction) {
|
|
$logInteraction("Nuevo miembro detectado", ['user_id' => $member->id, 'username' => $member->username, 'guild_id' => $member->guild_id]);
|
|
|
|
try {
|
|
$welcomeConfig = getWelcomeConfig($db);
|
|
|
|
if (!$welcomeConfig || !(bool)$welcomeConfig['activo']) {
|
|
$logInteraction("Bienvenida de Discord deshabilitada o no configurada.");
|
|
return;
|
|
}
|
|
|
|
// Verificar si el usuario ya está registrado en nuestra DB
|
|
if (isDiscordUserRegistered($db, $member->id)) {
|
|
$logInteraction("Miembro ya registrado, no se envía bienvenida para evitar duplicados.", ['user_id' => $member->id]);
|
|
return;
|
|
}
|
|
|
|
// Si la configuración indica registrar al usuario, hacerlo aquí
|
|
if ((bool)$welcomeConfig['registrar_usuario']) {
|
|
registerDiscordRecipient($db, $member->id, 'usuario', $member->username, $member->username);
|
|
$logInteraction("Nuevo miembro registrado en DB.", ['user_id' => $member->id]);
|
|
}
|
|
|
|
// Obtener el canal de bienvenida
|
|
$guild = $discord->guilds->get('id', $member->guild_id);
|
|
if (!$guild) {
|
|
$logInteraction("No se pudo obtener el gremio para enviar la bienvenida.", ['guild_id' => $member->guild_id]);
|
|
return;
|
|
}
|
|
$channel = $guild->channels->get('id', $welcomeConfig['canal_id']);
|
|
if (!$channel) {
|
|
$logInteraction("No se pudo encontrar el canal de bienvenida configurado.", ['channel_id' => $welcomeConfig['canal_id']]);
|
|
return;
|
|
}
|
|
|
|
// Construir el mensaje de bienvenida
|
|
$messageContent = $welcomeConfig['texto'];
|
|
// Reemplazar placeholders y limpiar HTML
|
|
$messageContent = str_replace('{usuario}', '<@'.$member->id.'>', $messageContent);
|
|
$messageContent = strip_tags($messageContent); // Eliminar HTML si existe
|
|
|
|
$messageBuilder = MessageBuilder::new()
|
|
->setContent($messageContent);
|
|
|
|
// Añadir imagen si está configurada
|
|
if (!empty($welcomeConfig['imagen_ruta'])) {
|
|
// Asumo que 'imagen_ruta' es una URL completa o una ruta accesible por Discord
|
|
// Si es una ruta local, necesitarías subirla a Discord primero o servirla desde una URL
|
|
// Por simplicidad, si es una ruta local, la ignoraremos por ahora a menos que se especifique cómo servirla.
|
|
// Si la ruta ya es una URL pública, se puede añadir directamente
|
|
if (filter_var($welcomeConfig['imagen_ruta'], FILTER_VALIDATE_URL)) {
|
|
$messageBuilder->setImage($welcomeConfig['imagen_ruta']);
|
|
} elseif (!empty($_ENV['CAOS_BASE_URL']) && strpos($welcomeConfig['imagen_ruta'], '/gallery/uploads/') === 0) {
|
|
// Si es una ruta relativa de galería, construir la URL completa
|
|
$fullImageUrl = rtrim($_ENV['CAOS_BASE_URL'], '/') . $welcomeConfig['imagen_ruta'];
|
|
$messageBuilder->setImage($fullImageUrl);
|
|
$logInteraction("Imagen de bienvenida construida como URL.", ['url' => $fullImageUrl]);
|
|
} else {
|
|
$logInteraction("Ruta de imagen no válida o no URL, se ignorará.", ['ruta' => $welcomeConfig['imagen_ruta']]);
|
|
}
|
|
}
|
|
|
|
// Añadir botones de idioma
|
|
$idiomasHabilitados = json_decode($welcomeConfig['idiomas_habilitados'], true) ?? [];
|
|
if (!empty($idiomasHabilitados)) {
|
|
$actionRow = new ActionRow();
|
|
$langLabels = [
|
|
'es' => 'Español',
|
|
'en' => 'English',
|
|
'pt' => 'Português'
|
|
];
|
|
foreach ($idiomasHabilitados as $langCode) {
|
|
$label = $langLabels[$langCode] ?? strtoupper($langCode);
|
|
$emoji = ''; // Puedes añadir emojis específicos si los tienes
|
|
if ($langCode === 'es') $emoji = '🇪🇸';
|
|
if ($langCode === 'en') $emoji = '🇬🇧';
|
|
if ($langCode === 'pt') $emoji = '🇵🇹';
|
|
|
|
$button = Button::new(Button::STYLE_PRIMARY)
|
|
->setLabel($label)
|
|
->setCustomId("lang_select_{$langCode}");
|
|
if (!empty($emoji)) {
|
|
$button->setEmoji($emoji);
|
|
}
|
|
$actionRow->addComponent($button);
|
|
}
|
|
$messageBuilder->addComponent($actionRow);
|
|
}
|
|
|
|
// Enviar el mensaje de bienvenida (no efímero)
|
|
$channel->sendMessage($messageBuilder)
|
|
->then(function ($message) use ($member, $logInteraction) {
|
|
$logInteraction("Mensaje de bienvenida enviado con éxito.", ['user_id' => $member->id, 'message_id' => $message->id]);
|
|
})
|
|
->otherwise(function ($error) use ($member, $logInteraction) {
|
|
$logInteraction("Error al enviar mensaje de bienvenida.", ['user_id' => $member->id, 'error' => $error->getMessage()]);
|
|
});
|
|
|
|
} catch (\Throwable $e) {
|
|
$logInteraction("Error en manejador GUILD_MEMBER_ADD.", ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
|
|
}
|
|
});
|
|
|
|
// --- Manejador de interacciones (botones, etc.) ---
|
|
$discord->on('INTERACTION_CREATE', function (Interaction $interaction, Discord $discord) use ($db, $logInteraction) {
|
|
// Loguear cada interacción recibida
|
|
$logInteraction("Interacción recibida en INTERACTION_CREATE", [
|
|
'id' => $interaction->id ?? 'N/A',
|
|
'type' => $interaction->type ?? 'N/A',
|
|
'custom_id' => $interaction->data->custom_id ?? 'N/A',
|
|
'user_id' => $interaction->user->id ?? 'N/A',
|
|
'command_name' => $interaction->data->name ?? 'N/A' // Añadir para comandos slash
|
|
]);
|
|
|
|
try {
|
|
// Manejar comandos de aplicación (slash commands)
|
|
if ($interaction->type === Interaction::TYPE_APPLICATION_COMMAND) {
|
|
$commandName = $interaction->data->name;
|
|
$logInteraction("Comando de aplicación recibido", ['command' => $commandName]);
|
|
|
|
if ($commandName === 'start') {
|
|
$welcomeConfig = getWelcomeConfig($db); // Obtener la configuración de bienvenida
|
|
$logInteraction("Comando /start: Obtenida configuración de bienvenida.", ['config' => $welcomeConfig]);
|
|
|
|
$messageContent = "¡Hola! Selecciona tu idioma preferido para interactuar con el bot."; // Default
|
|
$imageUrl = null;
|
|
$idiomasHabilitados = [];
|
|
|
|
if ($welcomeConfig && (bool)$welcomeConfig['activo']) {
|
|
$messageContent = $welcomeConfig['texto'];
|
|
// Reemplazar placeholders y limpiar HTML
|
|
$messageContent = str_replace('{usuario}', '<@'.$interaction->user->id.'>', $messageContent);
|
|
$messageContent = strip_tags($messageContent); // Eliminar HTML si existe
|
|
|
|
// Añadir imagen si está configurada
|
|
if (!empty($welcomeConfig['imagen_ruta'])) {
|
|
if (filter_var($welcomeConfig['imagen_ruta'], FILTER_VALIDATE_URL)) {
|
|
$imageUrl = $welcomeConfig['imagen_ruta'];
|
|
} elseif (!empty($_ENV['CAOS_BASE_URL']) && strpos($welcomeConfig['imagen_ruta'], '/gallery/uploads/') === 0) {
|
|
$imageUrl = rtrim($_ENV['CAOS_BASE_URL'], '/') . $welcomeConfig['imagen_ruta'];
|
|
}
|
|
}
|
|
$idiomasHabilitados = json_decode($welcomeConfig['idiomas_habilitados'], true) ?? [];
|
|
}
|
|
|
|
// Mensaje de bienvenida (texto y posible imagen)
|
|
$welcomeMessageBuilder = MessageBuilder::new()
|
|
->setContent($messageContent)
|
|
->setFlags(Message::FLAG_EPHEMERAL);
|
|
|
|
if ($imageUrl) {
|
|
$welcomeMessageBuilder->setImage($imageUrl);
|
|
}
|
|
|
|
// Enviar el mensaje de bienvenida efímero como respuesta inicial
|
|
$interaction->respondWithMessage($welcomeMessageBuilder, true);
|
|
$logInteraction("Comando /start manejado: mensaje de bienvenida enviado (respondWithMessage efímero).", ['content' => $messageContent, 'image' => $imageUrl]);
|
|
|
|
// Crear los botones de selección de idioma
|
|
$buttons = [];
|
|
$langLabels = [
|
|
'es' => 'Español',
|
|
'en' => 'English',
|
|
'pt' => 'Português'
|
|
];
|
|
foreach ($idiomasHabilitados as $langCode) {
|
|
$label = $langLabels[$langCode] ?? strtoupper($langCode);
|
|
$emoji = '';
|
|
if ($langCode === 'es') $emoji = '🇪🇸';
|
|
if ($langCode === 'en') $emoji = '🇬🇧';
|
|
if ($langCode === 'pt') $emoji = '🇵🇹';
|
|
|
|
$button = Button::new(Button::STYLE_PRIMARY)
|
|
->setLabel($label)
|
|
->setCustomId("lang_select_{$langCode}");
|
|
if (!empty($emoji)) {
|
|
$button->setEmoji($emoji);
|
|
}
|
|
$buttons[] = $button;
|
|
}
|
|
|
|
$actionRow = new ActionRow();
|
|
foreach ($buttons as $button) {
|
|
$actionRow->addComponent($button);
|
|
}
|
|
|
|
// Enviar los botones como un follow-up message efímero
|
|
$buttonsMessage = MessageBuilder::new()
|
|
->addComponent($actionRow)
|
|
->setFlags(Message::FLAG_EPHEMERAL);
|
|
|
|
$interaction->sendFollowUpMessage($buttonsMessage);
|
|
$logInteraction("Comando /start manejado: botones de idioma enviados (sendFollowUpMessage efímero).");
|
|
return;
|
|
}
|
|
// Puedes añadir más comandos slash aquí
|
|
}
|
|
|
|
// Manejar componentes de mensaje (botones, etc.)
|
|
if ($interaction->type === Interaction::TYPE_MESSAGE_COMPONENT) {
|
|
$customId = $interaction->data->custom_id ?? '';
|
|
|
|
// Manejar botón de traducción
|
|
if (strpos($customId, 'translate_') === 0) {
|
|
$logInteraction("Manejando botón de traducción", [
|
|
'custom_id' => $customId,
|
|
'message_id' => str_replace('translate_', '', $customId)
|
|
]);
|
|
|
|
try {
|
|
// Asegurarse de que la interacción se reconozca
|
|
$interaction->acknowledge(true);
|
|
|
|
// Llamar al manejador de traducción
|
|
handleTranslationButton($interaction, $db);
|
|
} catch (\Throwable $e) {
|
|
$logInteraction("Error al manejar el botón de traducción", [
|
|
'error' => $e->getMessage(),
|
|
'trace' => $e->getTraceAsString()
|
|
]);
|
|
|
|
// Intentar notificar al usuario del error
|
|
try {
|
|
$interaction->sendMessage(
|
|
MessageBuilder::new()
|
|
->setContent("❌ Ocurrió un error al procesar la traducción. Por favor, inténtalo de nuevo.")
|
|
->setFlags(Message::FLAG_EPHEMERAL),
|
|
true
|
|
);
|
|
} catch (\Throwable $e2) {
|
|
$logInteraction("Error al notificar al usuario", [
|
|
'error' => $e2->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Manejar selección de idioma
|
|
if (strpos($customId, 'lang_select_') === 0) {
|
|
$logInteraction("Entrando a lang_select_ handler", ['custom_id' => $customId]);
|
|
// Primero, reconocer la interacción para evitar el timeout de Discord
|
|
$interaction->acknowledge(true); // true para una respuesta efímera
|
|
$logInteraction("Interacción acknowledge(true) ejecutada.");
|
|
|
|
$langCode = substr($customId, strlen('lang_select_'));
|
|
$user = $interaction->user;
|
|
error_log("Usuario {$user->username} seleccionó idioma: {$langCode}" . PHP_EOL, 3, __DIR__ . '/logs/interaction_events.log');
|
|
|
|
// Antes de la DB:
|
|
$logInteraction("Intentando registrar/actualizar destinatario en BD.", ['user_id' => $user->id, 'lang_code' => $langCode]);
|
|
registerDiscordRecipient($db, $user->id, 'usuario', $user->username, $user->username);
|
|
$stmt = $db->prepare("UPDATE destinatarios_discord SET idioma_detectado = ? WHERE discord_id = ?");
|
|
$stmt->execute([$langCode, $user->id]);
|
|
// Después de la DB:
|
|
$logInteraction("Destinatario registrado/actualizado en BD.");
|
|
|
|
error_log("Preferencia de idioma guardada en BD." . PHP_EOL, 3, __DIR__ . '/logs/interaction_events.log');
|
|
$interaction->sendFollowUpMessage( // <-- CAMBIO AQUÍ
|
|
MessageBuilder::new()
|
|
->setContent("✅ Idioma seleccionado: " . strtoupper($langCode))
|
|
->setFlags(Message::FLAG_EPHEMERAL)
|
|
);
|
|
$logInteraction("sendFollowUpMessage ejecutado en lang_select_ handler.");
|
|
return;
|
|
}
|
|
}
|
|
} catch (\Throwable $e) {
|
|
error_log("Error en INTERACTION_CREATE: " . $e->getMessage());
|
|
|
|
// Intentar responder con un mensaje de error si es posible
|
|
if (isset($interaction) && $interaction instanceof Interaction) {
|
|
try {
|
|
$interaction->respondWithMessage(
|
|
MessageBuilder::new()
|
|
->setContent("❌ Ocurrió un error al procesar tu solicitud.")
|
|
->setFlags(Message::FLAG_EPHEMERAL)
|
|
);
|
|
} catch (\Throwable $e2) {
|
|
// Ignorar errores al intentar responder
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// --- Manejador de errores de WebSocket ---
|
|
$discord->on('error', function ($error, $ws) use ($discord) {
|
|
echo "Error de WebSocket: " . $error . PHP_EOL;
|
|
// Intentar reconectar después de 5 segundos
|
|
$discord->getLoop()->addTimer(5, function() use ($discord) {
|
|
echo "Intentando reconectar..." . PHP_EOL;
|
|
$discord->getLoop()->stop();
|
|
});
|
|
});
|
|
|
|
// --- Función para detectar si un mensaje necesita traducción ---
|
|
function needsTranslation($text) {
|
|
// Si el texto está vacío o es muy corto, no necesita traducción
|
|
if (empty(trim($text)) || strlen(trim($text)) < 2) {
|
|
return false;
|
|
}
|
|
|
|
// Lista de palabras comunes en español
|
|
$spanishWords = [
|
|
'hola', 'buenos', 'días', 'tardes', 'noche', 'gracias', 'por favor',
|
|
'adiós', 'hasta luego', 'sí', 'no', 'porque', 'pero', 'y', 'o',
|
|
'el', 'la', 'los', 'las', 'un', 'una', 'unos', 'unas', 'es', 'son',
|
|
'soy', 'eres', 'somos', 'sois', 'estoy', 'estás', 'está', 'estamos',
|
|
'estáis', 'están', 'tengo', 'tienes', 'tiene', 'tenemos', 'tenéis',
|
|
'tienen', 'voy', 'vas', 'va', 'vamos', 'vais', 'van', 'hacer', 'hecho',
|
|
'día', 'año', 'vez', 'tiempo', 'casa', 'agua', 'comida', 'bebida'
|
|
];
|
|
|
|
// Convertir a minúsculas y eliminar signos de puntuación
|
|
$cleanText = preg_replace('/[^\p{L}\s]/u', '', mb_strtolower($text));
|
|
$words = preg_split('/\s+/', $cleanText);
|
|
|
|
// Contar palabras en español
|
|
$spanishWordCount = 0;
|
|
foreach ($words as $word) {
|
|
if (in_array($word, $spanishWords)) {
|
|
$spanishWordCount++;
|
|
}
|
|
}
|
|
|
|
// Si menos del 30% de las palabras son en español, asumir que necesita traducción
|
|
$needsTranslation = ($spanishWordCount / max(1, count($words))) < 0.3;
|
|
|
|
error_log("Análisis de traducción - Palabras: " . count($words) . ", Español: $spanishWordCount, Necesita traducción: " . ($needsTranslation ? 'Sí' : 'No'));
|
|
|
|
return $needsTranslation;
|
|
}
|
|
|
|
// --- Función para traducir texto usando LibreTranslate ---
|
|
function translateText($text, $targetLang) {
|
|
$libreTranslateUrl = $_ENV['LIBRETRANSLATE_URL'] ?? 'http://localhost:5000';
|
|
$url = rtrim($libreTranslateUrl, '/') . '/translate';
|
|
|
|
$data = [
|
|
'q' => $text,
|
|
'source' => 'auto', // Detección automática del idioma
|
|
'target' => $targetLang
|
|
];
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($response === false || $httpCode !== 200) {
|
|
error_log("Error en la traducción. Código HTTP: $httpCode. Respuesta: " . $response);
|
|
return "Error al traducir el mensaje. Por favor, inténtalo de nuevo más tarde.";
|
|
}
|
|
|
|
$result = json_decode($response, true);
|
|
return $result['translatedText'] ?? $text;
|
|
}
|
|
|
|
// --- Función para manejar el botón de traducción ---
|
|
function handleTranslationButton(Interaction $interaction, PDO $db) {
|
|
try {
|
|
// Verificar que la interacción sea válida
|
|
if (!$interaction instanceof \Discord\Parts\Interactions\Interaction) {
|
|
$logInteraction("handleTranslationButton: Interacción no válida", ['type' => gettype($interaction)]);
|
|
return;
|
|
}
|
|
// Verificar que tengamos un ID de mensaje
|
|
if (empty($interaction->data->custom_id)) {
|
|
$logInteraction("handleTranslationButton: No se pudo obtener el ID del mensaje de la interacción", [
|
|
'interaction' => [
|
|
'id' => $interaction->id ?? 'N/A',
|
|
'type' => $interaction->type ?? 'N/A',
|
|
'data' => $interaction->data ?? 'N/A'
|
|
]
|
|
]);
|
|
throw new \Exception("No se pudo obtener el ID del mensaje de la interacción");
|
|
}
|
|
// Extraer el ID del mensaje original del custom_id
|
|
$customId = $interaction->data->custom_id;
|
|
$logError("handleTranslationButton: customId recibido (RAW): " . $customId, ['customId' => $customId]); // Nuevo log para el customId RAW
|
|
$logError("handleTranslationButton: customId recibido (desde variable): " . $customId, ['customId' => $customId]); // Mantener el log anterior para comparación
|
|
|
|
// El customId ahora es "translate_{messageId}"
|
|
$parts = explode('_', $customId);
|
|
$logError("handleTranslationButton: Resultado de explode: ", ['parts' => $parts, 'count' => count($parts)]);
|
|
if (count($parts) === 2 && $parts[0] === 'translate') {
|
|
$messageId = $parts[1];
|
|
} else {
|
|
$logError("handleTranslationButton: custom_id no tiene el formato esperado (translate_MESSAGEID).", ['customId' => $customId, 'parts' => $parts, 'count' => count($parts)]); $messageId = $parts[1];
|
|
$logInteraction("handleTranslationButton: custom_id no tiene el formato esperado (translate_MESSAGEID).", [
|
|
'customId' => $customId, 'parts' => $parts, 'count' => count($parts)]);
|
|
try {
|
|
return $interaction->sendFollowUpMessage(
|
|
MessageBuilder::new()
|
|
->setContent("❌ Error interno: Formato de botón de traducción incorrecto.")
|
|
->setFlags(Message::FLAG_EPHEMERAL)
|
|
);
|
|
} catch (\Exception $e) {
|
|
$logInteraction("Error al enviar mensaje de error por formato de botón.", ['error' => $e->getMessage()]
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Obtener el idioma del usuario que presionó el botón
|
|
$targetLang = getUserPreferredLanguage($db, $interaction->user->id);
|
|
if (empty($targetLang)) {
|
|
$targetLang = 'en'; // Default a inglés si no hay idioma preferido
|
|
$logInteraction("handleTranslationButton: Usuario {$interaction->user->id} no tiene idioma preferido, usando 'en'.");
|
|
}
|
|
$logInteraction("handleTranslationButton: Usuario {$interaction->user->username} (${interaction->user->id}) solicitó traducción a: {$targetLang}.");
|
|
|
|
|
|
// Obtener el canal donde ocurrió la interacción
|
|
$channel = $interaction->channel;
|
|
|
|
// Obtener el mensaje original
|
|
$message = null;
|
|
$error = null;
|
|
|
|
try {
|
|
|
|
// Intentar obtener el mensaje directamente del canal
|
|
|
|
$logInteraction("handleTranslationButton: Intentando obtener mensaje con ID: {$messageId} del canal: {$channel->id}.");
|
|
|
|
$message = $channel->getMessage($messageId);
|
|
|
|
$logInteraction("handleTranslationButton: Resultado de getMessage: " . ($message ? "Obtenido de caché" : "No en caché"));
|
|
|
|
|
|
|
|
// Si no se pudo obtener, intentar de otra manera
|
|
|
|
if (!$message) {
|
|
|
|
$logInteraction("handleTranslationButton: No se pudo obtener el mensaje directamente de caché, intentando con fetch a la API...");
|
|
|
|
$message = $channel->fetchMessage($messageId);
|
|
|
|
$logInteraction("handleTranslationButton: Resultado de fetchMessage: " . ($message ? "Obtenido de API" : "No obtenido de API"));
|
|
|
|
}
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
$error = $e->getMessage();
|
|
|
|
$logInteraction("handleTranslationButton: Error al obtener el mensaje", [
|
|
|
|
'error' => $error,
|
|
|
|
'messageId' => $messageId,
|
|
|
|
'channelId' => $channel->id ?? 'N/A'
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$message) {
|
|
|
|
$logInteraction("handleTranslationButton: No se pudo encontrar el mensaje original", [
|
|
|
|
'messageId' => $messageId,
|
|
|
|
'channelId' => $channel->id ?? 'N/A',
|
|
|
|
'error' => $error ?? 'Mensaje nulo sin error específico'
|
|
|
|
]);
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
return $interaction->sendFollowUpMessage(
|
|
|
|
|
|
|
|
MessageBuilder::new()
|
|
|
|
|
|
|
|
->setContent("❌ No se pudo encontrar el mensaje original (ID: {$messageId}). Puede haber
|
|
|
|
|
|
|
|
sido eliminado o ser demasiado antiguo.")
|
|
|
|
|
|
|
|
->setFlags(Message::FLAG_EPHEMERAL)
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
|
|
$logInteraction("Error al enviar mensaje de error al usuario", [
|
|
|
|
|
|
|
|
'error' => $e->getMessage()
|
|
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$logInteraction("handleTranslationButton: Mensaje original obtenido. Contenido: " . substr($message->content, 0
|
|
|
|
|
|
|
|
, 50) . "...");
|
|
|
|
|
|
|
|
|
|
// Traducir el mensaje al idioma del usuario que presionó el botón
|
|
$translatedText = translateText($message->content, $targetLang);
|
|
$logInteraction("handleTranslationButton: Mensaje traducido. Contenido: " . substr($translatedText, 0, 50) . "...");
|
|
|
|
// Crear el mensaje de respuesta
|
|
$langLabels = [
|
|
'es' => 'español',
|
|
'en' => 'inglés',
|
|
'pt' => 'portugués'
|
|
];
|
|
$displayLang = $langLabels[$targetLang] ?? $targetLang; // Fallback a usar el código si no está en la lista
|
|
|
|
$response = MessageBuilder::new()
|
|
->setContent("**Mensaje traducido al {$displayLang}**:\n\n" . $translatedText)
|
|
->setFlags(Message::FLAG_EPHEMERAL);
|
|
|
|
// Enviar la respuesta como un follow-up
|
|
$interaction->sendFollowUpMessage($response);
|
|
|
|
error_log("handleTranslationButton: Traducción enviada para el mensaje: " . $message->id);
|
|
|
|
} catch (\Throwable $e) {
|
|
$logInteraction("Error en handleTranslationButton (nivel superior): " . $e->getMessage(), ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
|
|
try {
|
|
$interaction->respondWithMessage(
|
|
MessageBuilder::new()->setContent("Ocurrió un error al traducir el mensaje."),
|
|
true
|
|
);
|
|
} catch (\Throwable $e2) {
|
|
// Ignorar errores al responder
|
|
}
|
|
}
|
|
}
|
|
// --- Función para obtener la configuración de bienvenida de Discord ---
|
|
function getWelcomeConfig(PDO $db): ?array {
|
|
$stmt = $db->query("
|
|
SELECT b.*, g.ruta as imagen_ruta
|
|
FROM bienvenida_discord b
|
|
LEFT JOIN gallery g ON b.imagen_id = g.id
|
|
LIMIT 1
|
|
");
|
|
$config = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
return $config ?: null;
|
|
}
|
|
|
|
// --- Función para verificar si un usuario de Discord ya está registrado ---
|
|
function isDiscordUserRegistered(PDO $db, string $discordId): bool {
|
|
$stmt = $db->prepare("SELECT COUNT(*) FROM destinatarios_discord WHERE discord_id = ?");
|
|
$stmt->execute([$discordId]);
|
|
return $stmt->fetchColumn() > 0;
|
|
}
|
|
|
|
// --- Función para obtener conexión a la base de datos ---
|
|
function getDBConnection(): PDO {
|
|
$host = $_ENV['DB_HOST'] ?? '127.0.0.1';
|
|
$db = $_ENV['DB_NAME'] ?? 'db';
|
|
$user = $_ENV['DB_USER'] ?? 'root';
|
|
$pass = $_ENV['DB_PASS'] ?? '';
|
|
$port = $_ENV['DB_PORT'] ?? '3306';
|
|
$charset = 'utf8mb4';
|
|
$dsn = "mysql:host=$host;port=$port;dbname=$db;charset=$charset";
|
|
$options = [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
PDO::ATTR_EMULATE_PREPARES => false,
|
|
];
|
|
return new PDO($dsn, $user, $pass, $options);
|
|
}
|
|
|
|
// --- Función para registrar destinatarios de Discord ---
|
|
function registerDiscordRecipient(PDO $db, $discordId, $type, $name, $username = null) {
|
|
$stmt = $db->prepare("INSERT INTO destinatarios_discord (discord_id, tipo, nombre, username)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE nombre = VALUES(nombre), username = VALUES(username)");
|
|
$stmt->execute([$discordId, $type, $name, $username]);
|
|
}
|
|
|
|
// --- Iniciar el bot ---
|
|
echo "Iniciando bot..." . PHP_EOL;
|
|
$discord->run();
|
|
|
|
// --- Manejar cierre limpio ---
|
|
register_shutdown_function(function() use ($discord, $db) {
|
|
echo "Cerrando el bot..." . PHP_EOL;
|
|
if ($db) {
|
|
$db = null;
|
|
}
|
|
$discord->close();
|
|
});
|