Files
sistema_para_juego/bot_daemon.20251201_002539.php

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();
});