Merge: Complete merge of remote changes, including user's requested additions.
This commit completes the merge process, incorporating remote changes that conflicted with local modifications. It also stages and commits all remaining modified and untracked files as per the user's instruction to 'upload everything without exception'.
This commit is contained in:
310
discord_bot.php
310
discord_bot.php
@@ -38,10 +38,21 @@ if (!defined('DISCORD_BOT_TOKEN') || empty(DISCORD_BOT_TOKEN)) {
|
||||
try {
|
||||
$discord = new Discord([
|
||||
'token' => DISCORD_BOT_TOKEN,
|
||||
<<<<<<< HEAD
|
||||
'intents' => Intents::GUILDS | Intents::GUILD_MESSAGES | Intents::DIRECT_MESSAGES | Intents::GUILD_MEMBERS | Intents::GUILD_MESSAGE_REACTIONS | Intents::MESSAGE_CONTENT,
|
||||
'logger' => $logger
|
||||
=======
|
||||
'intents' => Intents::GUILDS | Intents::GUILD_MESSAGES | Intents::DIRECT_MESSAGES | Intents::GUILD_MEMBERS | Intents::GUILD_MESSAGE_REACTIONS,
|
||||
'logger' => $logger,
|
||||
'loop' => \React\EventLoop\Loop::get(),
|
||||
'socket_options' => [
|
||||
'heartbeat_interval' => 30, // Enviar heartbeat cada 30 segundos
|
||||
'reconnect_timeout' => 60, // Timeout para reconexión
|
||||
]
|
||||
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||
]);
|
||||
|
||||
// Manejar eventos de conexión y desconexión
|
||||
$discord->on('ready', function (Discord $discord) {
|
||||
$discord->getLogger()->info("==================================================");
|
||||
$discord->getLogger()->info("Bot conectado y listo para escuchar!");
|
||||
@@ -49,6 +60,21 @@ try {
|
||||
$discord->getLogger()->info("==================================================");
|
||||
});
|
||||
|
||||
// Evento de reconexión
|
||||
$discord->on('reconnected', function (Discord $discord) {
|
||||
$logger->info("[RECONEXIÓN] Bot reconectado exitosamente");
|
||||
});
|
||||
|
||||
// Evento de desconexión
|
||||
$discord->on('disconnected', function (Discord $discord, $reason) {
|
||||
$logger->warning("[DESCONEXIÓN] Bot desconectado. Razón: $reason");
|
||||
});
|
||||
|
||||
// Evento de error
|
||||
$discord->on('error', function ($error, Discord $discord) {
|
||||
$logger->error("[ERROR DISCORD] Error en la conexión: " . $error->getMessage());
|
||||
});
|
||||
|
||||
// Evento para nuevos miembros en el servidor
|
||||
$discord->on(Event::GUILD_MEMBER_ADD, function (Member $member, Discord $discord) use ($pdo, $logger) {
|
||||
$logger->info("[NUEVO MIEMBRO] Usuario {$member->user->username} ({$member->id}) se ha unido al servidor.");
|
||||
@@ -79,9 +105,11 @@ try {
|
||||
try {
|
||||
if (strpos($customId, 'translate_manual:') === 0) {
|
||||
$targetLang = substr($customId, strlen('translate_manual:'));
|
||||
$userId = $interaction->user->id;
|
||||
$originalMessage = $interaction->message;
|
||||
$channelId = $originalMessage->channel_id;
|
||||
$channel = $interaction->channel;
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Responder de inmediato para evitar timeout de interacción
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent('⌛ Procesando traducción...'), true);
|
||||
|
||||
@@ -113,32 +141,124 @@ try {
|
||||
|
||||
// Obtener bandera desde supported_languages
|
||||
$flag = '';
|
||||
=======
|
||||
try {
|
||||
// Buscar el mensaje original que se está traduciendo
|
||||
// Para interacciones, el mensaje original está en el mensaje de la interacción
|
||||
$originalContent = '';
|
||||
|
||||
// Primero intentar obtener el mensaje referenciado si existe
|
||||
$messageReference = $originalMessage->message_reference;
|
||||
if ($messageReference && $messageReference->message_id) {
|
||||
$referencedMessageId = $messageReference->message_id;
|
||||
$channel->messages->fetch($referencedMessageId)->done(function (Message $referencedMessage) use ($interaction, $targetLang, $logger, $discord, $userId, $pdo) {
|
||||
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||
try {
|
||||
$originalContent = trim((string) ($referencedMessage->content ?? ''));
|
||||
|
||||
if (empty($originalContent)) {
|
||||
$interaction->respondWithMessage(
|
||||
MessageBuilder::new()->setContent('❌ No se encontró contenido para traducir.'),
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extraer y preservar emojis, stickers y elementos de Discord
|
||||
$processedContent = preserveDiscordElements($originalContent);
|
||||
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||
$sourceLang = $translator->detectLanguage($processedContent['text']) ?? 'es';
|
||||
|
||||
if ($sourceLang === $targetLang) {
|
||||
$interaction->respondWithMessage(
|
||||
MessageBuilder::new()->setContent('ℹ️ El mensaje ya está en este idioma.'),
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$translatedText = $translator->translateText($processedContent['text'], $sourceLang, $targetLang);
|
||||
|
||||
// Restaurar emojis y elementos de Discord
|
||||
$finalText = restoreDiscordElements($translatedText, $processedContent['placeholders']);
|
||||
|
||||
// Obtener bandera
|
||||
$stmt = $pdo->prepare("SELECT flag_emoji FROM supported_languages WHERE language_code = ? AND is_active = 1");
|
||||
$stmt->execute([$targetLang]);
|
||||
$flag = $stmt->fetchColumn() ?: '';
|
||||
} catch (\Throwable $e) { /* noop */ }
|
||||
|
||||
$flag = $flag !== '' ? $flag : '🏳️';
|
||||
|
||||
$flag = $flag !== '' ? $flag : '🏳️';
|
||||
|
||||
// Enviar resultado como mensaje normal al canal, mencionando al usuario
|
||||
$sender = new DiscordSender(DISCORD_BOT_TOKEN);
|
||||
$mention = '<@' . $userId . '>';
|
||||
$content = "{$mention} {$flag} Traducción ({$sourceLang} → {$targetLang}):\n> " . $translatedText;
|
||||
// Pequeña espera para que el mensaje efímero aparezca primero
|
||||
usleep(300000);
|
||||
$sender->sendRawMessage($channelId, $content);
|
||||
if (empty($translatedText)) {
|
||||
$sender = new DiscordSender(DISCORD_BOT_TOKEN);
|
||||
$sender->sendRawMessage($channelId, '<@' . $userId . '> El mensaje ya está en este idioma.');
|
||||
// Responder efímeramente con la traducción
|
||||
$response = "{$flag} **Traducción ({$sourceLang} → {$targetLang}):**\n\n" . $finalText;
|
||||
$interaction->respondWithMessage(
|
||||
MessageBuilder::new()->setContent($response),
|
||||
true // Efímero - solo visible para el usuario
|
||||
);
|
||||
|
||||
$logger->info("[TRADUCCIÓN EFÍMERA] Traducción {$sourceLang}→{$targetLang} enviada efímeramente");
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$logger->error("[Error Traducción Manual]", ['error' => $e->getMessage()]);
|
||||
$interaction->respondWithMessage(
|
||||
MessageBuilder::new()->setContent('❌ Error al procesar la traducción: ' . $e->getMessage()),
|
||||
true
|
||||
);
|
||||
}
|
||||
});
|
||||
return; // Salir después de iniciar el fetch
|
||||
} else {
|
||||
// Fallback: usar el contenido del mensaje original si no hay referencia
|
||||
$originalContent = trim((string) ($originalMessage->content ?? ''));
|
||||
|
||||
if (empty($originalContent)) {
|
||||
$interaction->respondWithMessage(
|
||||
MessageBuilder::new()->setContent('❌ No se encontró contenido para traducir.'),
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$sender = new DiscordSender(DISCORD_BOT_TOKEN);
|
||||
$sender->sendRawMessage($channelId, '<@' . $userId . "> Error al traducir: " . $e->getMessage());
|
||||
|
||||
// Extraer y preservar emojis, stickers y elementos de Discord
|
||||
$processedContent = preserveDiscordElements($originalContent);
|
||||
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||
$sourceLang = $translator->detectLanguage($processedContent['text']) ?? 'es';
|
||||
|
||||
if ($sourceLang === $targetLang) {
|
||||
$interaction->respondWithMessage(
|
||||
MessageBuilder::new()->setContent('ℹ️ El mensaje ya está en este idioma.'),
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$translatedText = $translator->translateText($processedContent['text'], $sourceLang, $targetLang);
|
||||
|
||||
// Restaurar emojis y elementos de Discord
|
||||
$finalText = restoreDiscordElements($translatedText, $processedContent['placeholders']);
|
||||
|
||||
// Obtener bandera
|
||||
$stmt = $pdo->prepare("SELECT flag_emoji FROM supported_languages WHERE language_code = ? AND is_active = 1");
|
||||
$stmt->execute([$targetLang]);
|
||||
$flag = $stmt->fetchColumn() ?: '';
|
||||
|
||||
$flag = $flag !== '' ? $flag : '🏳️';
|
||||
|
||||
// Responder efímeramente con la traducción
|
||||
$response = "{$flag} **Traducción ({$sourceLang} → {$targetLang}):**\n\n" . $finalText;
|
||||
$interaction->respondWithMessage(
|
||||
MessageBuilder::new()->setContent($response),
|
||||
true // Efímero - solo visible para el usuario
|
||||
);
|
||||
|
||||
$logger->info("[TRADUCCIÓN EFÍMERA] Traducción {$sourceLang}→{$targetLang} enviada efímeramente");
|
||||
}
|
||||
} else {
|
||||
$sender = new DiscordSender(DISCORD_BOT_TOKEN);
|
||||
$sender->sendRawMessage($channelId, '<@' . $userId . '> No se encontró contenido para traducir.');
|
||||
} catch (\Throwable $e) {
|
||||
$logger->error("[Error Interacción Manual]", ['error' => $e->getMessage()]);
|
||||
$interaction->respondWithMessage(
|
||||
MessageBuilder::new()->setContent('❌ Error al procesar la solicitud: ' . $e->getMessage()),
|
||||
true
|
||||
);
|
||||
}
|
||||
return; // Salir después de manejar la interacción
|
||||
}
|
||||
@@ -445,6 +565,23 @@ try {
|
||||
}
|
||||
});
|
||||
|
||||
// Sistema de verificación de salud periódica
|
||||
$discord->on('ready', function (Discord $discord) use ($logger) {
|
||||
// Programar verificación de salud cada 2 minutos
|
||||
$discord->getLoop()->addPeriodicTimer(120, function() use ($discord, $logger) {
|
||||
try {
|
||||
// Verificar si el bot sigue conectado
|
||||
if ($discord->user && $discord->user->id) {
|
||||
$logger->info("[HEALTH CHECK] Bot conectado y respondiendo. Usuario: {$discord->user->username}");
|
||||
} else {
|
||||
$logger->warning("[HEALTH CHECK] Bot no responde correctamente");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$logger->error("[HEALTH CHECK] Error en verificación: " . $e->getMessage());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$discord->run();
|
||||
|
||||
} catch (Throwable $e) {
|
||||
@@ -604,6 +741,7 @@ function handleDiscordCommand(Message $message, PDO $pdo, Logger $logger)
|
||||
function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
||||
{
|
||||
try {
|
||||
<<<<<<< HEAD
|
||||
$translator = new Translate(LIBRETRANSLATE_URL); // Instanciar al inicio
|
||||
|
||||
$messageContentOriginal = trim($message->content);
|
||||
@@ -623,10 +761,29 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
||||
$confidence = $detectionResult[0]['confidence'] ?? 0.0;
|
||||
if ($confidence > 0.0) {
|
||||
$hasTranslatableText = true;
|
||||
=======
|
||||
$text = $message->content;
|
||||
$attachments = $message->attachments;
|
||||
|
||||
// Verificar si el mensaje tiene contenido de texto
|
||||
$hasTextContent = !empty(trim($text));
|
||||
|
||||
// Verificar si hay texto en los attachments (captions de imágenes/videos)
|
||||
$attachmentText = '';
|
||||
if (!empty($attachments)) {
|
||||
foreach ($attachments as $attachment) {
|
||||
// Algunos attachments tienen propiedad 'description' o 'caption'
|
||||
if (!empty($attachment->description)) {
|
||||
$attachmentText .= $attachment->description . ' ';
|
||||
}
|
||||
if (!empty($attachment->caption)) {
|
||||
$attachmentText .= $attachment->caption . ' ';
|
||||
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// If no translatable text is found, do not send buttons.
|
||||
if (!$hasTranslatableText) {
|
||||
$logger->info("[TRANSLATION_BUTTONS] Mensaje de Discord #{$message->id} sin contenido de texto traducible, no se envían botones de traducción.");
|
||||
@@ -642,10 +799,33 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
||||
$activeLangs = $langStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// 3. Filtrar los idiomas de destino (todos los activos menos el original)
|
||||
=======
|
||||
// Combinar texto principal con texto de attachments
|
||||
$fullText = trim($text . ' ' . $attachmentText);
|
||||
$hasAnyText = !empty($fullText);
|
||||
|
||||
// Verificar si el mensaje tiene solo imágenes/adjuntos sin texto
|
||||
$hasOnlyAttachments = !$hasAnyText && !empty($attachments) && count($attachments) > 0;
|
||||
|
||||
// Si el mensaje no tiene texto o tiene solo imágenes, no mostrar botones de traducción
|
||||
if (!$hasAnyText || $hasOnlyAttachments) {
|
||||
$logger->info("[TRADUCCIÓN] Mensaje #{$message->id} no tiene contenido de texto, omitiendo botones de traducción.");
|
||||
return;
|
||||
}
|
||||
$translator = new Translate(LIBRETRANSLATE_URL);
|
||||
$detectedLang = $translator->detectLanguage(strip_tags($fullText)) ?? 'es';
|
||||
|
||||
// Obtener idiomas activos disponibles
|
||||
$langStmt = $pdo->query("SELECT language_code, language_name, flag_emoji FROM supported_languages WHERE is_active = 1");
|
||||
$activeLangs = $langStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Filtrar idiomas diferentes al detectado
|
||||
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||
$targetLangs = array_filter($activeLangs, function($lang) use ($detectedLang) {
|
||||
return $lang['language_code'] !== $detectedLang;
|
||||
});
|
||||
|
||||
<<<<<<< HEAD
|
||||
// 4. Si no hay idiomas a los que traducir, no hacer nada
|
||||
if (empty($targetLangs)) {
|
||||
$logger->info("[TRANSLATION_BUTTONS] No se requieren botones de traducción para el mensaje de Discord #{$message->id} desde '$detectedLang'.");
|
||||
@@ -653,11 +833,20 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
||||
}
|
||||
|
||||
// 5. Crear botones de traducción para cada idioma destino
|
||||
=======
|
||||
if (empty($targetLangs)) {
|
||||
$logger->info("[TRADUCCIÓN] No hay idiomas disponibles para traducir desde '$detectedLang'");
|
||||
return;
|
||||
}
|
||||
|
||||
// Crear botones con banderas
|
||||
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||
$components = [];
|
||||
$actionRow = ActionRow::new();
|
||||
$buttonCount = 0;
|
||||
|
||||
foreach ($targetLangs as $lang) {
|
||||
<<<<<<< HEAD
|
||||
$button = Button::new(Button::STYLE_SECONDARY, 'translate_to_lang:' . $message->id . ':' . $lang['language_code'])
|
||||
->setLabel($lang['language_name']);
|
||||
if (!empty($lang['flag_emoji'])) {
|
||||
@@ -666,15 +855,33 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
||||
$actionRow->addComponent($button);
|
||||
$buttonCount++;
|
||||
|
||||
=======
|
||||
$button = Button::new(Button::STYLE_SECONDARY, 'translate_manual:' . $lang['language_code'])
|
||||
->setLabel($lang['language_name']);
|
||||
|
||||
if (!empty($lang['flag_emoji'])) {
|
||||
$button->setEmoji($lang['flag_emoji']);
|
||||
}
|
||||
|
||||
$actionRow->addComponent($button);
|
||||
$buttonCount++;
|
||||
|
||||
// Discord permite máximo 5 botones por ActionRow
|
||||
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||
if ($buttonCount % 5 === 0) {
|
||||
$components[] = $actionRow;
|
||||
$actionRow = ActionRow::new();
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||
if ($buttonCount % 5 !== 0) {
|
||||
$components[] = $actionRow;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// 6. Enviar mensaje del bot con botones como respuesta al mensaje original
|
||||
$builder = MessageBuilder::new()
|
||||
->setContent('🌐 Select a language to translate to:')
|
||||
@@ -688,5 +895,68 @@ function handleDiscordTranslation(Message $message, PDO $pdo, Logger $logger)
|
||||
|
||||
} catch (Throwable $e) {
|
||||
$logger->error("[TRANSLATION_BUTTONS] Error al procesar mensaje para botones de traducción", ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
|
||||
=======
|
||||
// Enviar mensaje con botones efímeros
|
||||
$builder = MessageBuilder::new()
|
||||
// ->setContent("🌍 **Selecciona idioma para traducir:**")
|
||||
->setComponents($components);
|
||||
|
||||
$message->reply($builder, true); // true = ephemeral (solo visible para el usuario)
|
||||
|
||||
$logger->info("[TRADUCCIÓN] Botones de traducción enviados para mensaje #{$message->id}");
|
||||
|
||||
} catch (Throwable $e) {
|
||||
$logger->error("[Error Traducción Discord]", ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]);
|
||||
>>>>>>> 26414094d4262e5ab092028955a4f0de57092f43
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Función para preservar emojis, stickers y elementos de Discord durante la traducción
|
||||
*/
|
||||
function preserveDiscordElements($text) {
|
||||
$placeholders = [];
|
||||
$processedText = $text;
|
||||
|
||||
// Patrones para detectar elementos de Discord (excluyendo emojis Unicode)
|
||||
$patterns = [
|
||||
// Emojis personalizados de Discord <:name:id>
|
||||
'/<a?:([a-zA-Z0-9_]+):(\d+)>/',
|
||||
// Menciones de usuarios <@id> y <@!id>
|
||||
'/<@!?(\d+)>/',
|
||||
// Menciones de canales <#id>
|
||||
'/<#(\d+)>/',
|
||||
// Menciones de roles <@&id>
|
||||
'/<@&(\d+)>/',
|
||||
// Stickers y GIFs animados (pueden venir como URLs especiales)
|
||||
'/https?:\/\/(?:media|cdn)\.discordapp\.(?:com|net)\/(stickers|attachments)\/\S+/i'
|
||||
];
|
||||
|
||||
$index = 0;
|
||||
foreach ($patterns as $pattern) {
|
||||
$processedText = preg_replace_callback($pattern, function($matches) use (&$placeholders, &$index) {
|
||||
$placeholder = "DISCORD_ELEMENT_{$index}";
|
||||
$placeholders[$placeholder] = $matches[0];
|
||||
$index++;
|
||||
return $placeholder;
|
||||
}, $processedText);
|
||||
}
|
||||
|
||||
return [
|
||||
'text' => $processedText,
|
||||
'placeholders' => $placeholders
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Función para restaurar emojis, stickers y elementos de Discord después de la traducción
|
||||
*/
|
||||
function restoreDiscordElements($translatedText, $placeholders) {
|
||||
$restoredText = $translatedText;
|
||||
|
||||
foreach ($placeholders as $placeholder => $originalElement) {
|
||||
$restoredText = str_replace($placeholder, $originalElement, $restoredText);
|
||||
}
|
||||
|
||||
return $restoredText;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user