Primer commit del sistema separado falta mejorar mucho
This commit is contained in:
209
Sistema_discord/HtmlToDiscordMarkdownConverter.php
Executable file
209
Sistema_discord/HtmlToDiscordMarkdownConverter.php
Executable file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
// Incluir el archivo de configuración
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
class HtmlToDiscordMarkdownConverter
|
||||
{
|
||||
private const DISCORD_MESSAGE_LIMIT = 2000;
|
||||
|
||||
public function convert(string $html): string
|
||||
{
|
||||
$dom = new DOMDocument();
|
||||
libxml_use_internal_errors(true); // Suppress warnings for malformed HTML
|
||||
// Use LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD to prevent adding html/body tags
|
||||
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
libxml_clear_errors();
|
||||
|
||||
$markdown = '';
|
||||
|
||||
foreach ($dom->childNodes as $node) {
|
||||
$markdown .= $this->processNode($node);
|
||||
}
|
||||
|
||||
// Clean up extra newlines
|
||||
$markdown = preg_replace('/\n{3,}/', "\n\n", $markdown);
|
||||
$markdown = trim($markdown);
|
||||
|
||||
return $markdown;
|
||||
}
|
||||
|
||||
private function processNode(DOMNode $node): string
|
||||
{
|
||||
$output = '';
|
||||
|
||||
switch ($node->nodeType) {
|
||||
case XML_TEXT_NODE:
|
||||
$output .= $this->decodeHtmlEntities($node->nodeValue);
|
||||
break;
|
||||
case XML_ELEMENT_NODE:
|
||||
switch (strtolower($node->nodeName)) {
|
||||
case 'b':
|
||||
case 'strong':
|
||||
$output .= '**' . $this->processChildren($node) . '**';
|
||||
break;
|
||||
case 'i':
|
||||
case 'em':
|
||||
$output .= '*' . $this->processChildren($node) . '*';
|
||||
break;
|
||||
case 'u':
|
||||
$output .= '__' . $this->processChildren($node) . '__';
|
||||
break;
|
||||
case 'a':
|
||||
$href = $node->getAttribute('href');
|
||||
|
||||
// Ignorar nodos de texto con solo espacios en blanco para un análisis más preciso.
|
||||
$realChildNodes = [];
|
||||
foreach ($node->childNodes as $child) {
|
||||
if ($child->nodeType === XML_TEXT_NODE && trim($child->nodeValue) === '') {
|
||||
continue;
|
||||
}
|
||||
$realChildNodes[] = $child;
|
||||
}
|
||||
|
||||
// Si el único hijo real es una imagen, procesarla directamente.
|
||||
if (count($realChildNodes) === 1 && strtolower($realChildNodes[0]->nodeName) === 'img') {
|
||||
$output .= $this->processChildren($node);
|
||||
} else {
|
||||
// Si no, trátalo como un enlace de texto normal.
|
||||
$text = $this->processChildren($node);
|
||||
$output .= "[{$text}]({$href})";
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
$output .= $this->processChildren($node) . "\n\n";
|
||||
break;
|
||||
case 'br':
|
||||
$output .= "\n";
|
||||
break;
|
||||
case 'ul':
|
||||
case 'ol':
|
||||
$listContent = $this->processChildren($node);
|
||||
$listItems = explode("\n", trim($listContent));
|
||||
$formattedList = [];
|
||||
$counter = 1;
|
||||
foreach($listItems as $item) {
|
||||
if(empty(trim($item))) continue;
|
||||
if (strtolower($node->nodeName) === 'ul') {
|
||||
$formattedList[] = '- ' . trim($item);
|
||||
} else {
|
||||
$formattedList[] = ($counter++) . '. ' . trim($item);
|
||||
}
|
||||
}
|
||||
$output .= implode("\n", $formattedList) . "\n\n";
|
||||
break;
|
||||
case 'li':
|
||||
$output .= $this->processChildren($node);
|
||||
break;
|
||||
case 'img':
|
||||
$src = $node->getAttribute('src');
|
||||
if (!empty($src)) {
|
||||
$absoluteImageUrl = $src;
|
||||
// Convertir URL relativa a absoluta si es necesario
|
||||
if (strpos($src, 'http') !== 0 && strpos($src, '//') !== 0) {
|
||||
$base = rtrim(BOT_BASE_URL, '/');
|
||||
$path = ltrim($src, '/');
|
||||
$absoluteImageUrl = "{$base}/{$path}";
|
||||
}
|
||||
// Dejar solo la URL para que Discord la renderice
|
||||
$output .= "\n" . $absoluteImageUrl . "\n";
|
||||
}
|
||||
break;
|
||||
case 'div':
|
||||
$output .= $this->processChildren($node);
|
||||
break;
|
||||
default:
|
||||
// For unknown tags, just process their children
|
||||
$output .= $this->processChildren($node);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function processChildren(DOMNode $node): string
|
||||
{
|
||||
$childrenOutput = '';
|
||||
foreach ($node->childNodes as $child) {
|
||||
$childrenOutput .= $this->processNode($child);
|
||||
}
|
||||
return $childrenOutput;
|
||||
}
|
||||
|
||||
private function decodeHtmlEntities(string $encodedString): string
|
||||
{
|
||||
return html_entity_decode($encodedString, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
|
||||
public function convertToArray(string $html): array
|
||||
{
|
||||
$parts = [];
|
||||
$dom = new DOMDocument();
|
||||
libxml_use_internal_errors(true);
|
||||
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
libxml_clear_errors();
|
||||
|
||||
foreach ($dom->childNodes as $node) {
|
||||
$this->processNodeForArray($node, $parts);
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
private function processNodeForArray(DOMNode $node, array &$parts)
|
||||
{
|
||||
if ($node->nodeType === XML_TEXT_NODE) {
|
||||
$this->addTextPart($parts, $this->decodeHtmlEntities($node->nodeValue));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node->nodeType !== XML_ELEMENT_NODE) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (strtolower($node->nodeName)) {
|
||||
case 'img':
|
||||
$src = $node->getAttribute('src');
|
||||
if (!empty($src)) {
|
||||
$absoluteImageUrl = $src;
|
||||
if (strpos($src, 'http') !== 0 && strpos($src, '//') !== 0) {
|
||||
$base = rtrim(BOT_BASE_URL, '/');
|
||||
$path = ltrim($src, '/');
|
||||
$absoluteImageUrl = "{$base}/{$path}";
|
||||
}
|
||||
$parts[] = ['type' => 'image', 'url' => $absoluteImageUrl];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
case 'div':
|
||||
if ($node->hasChildNodes()) {
|
||||
foreach ($node->childNodes as $child) {
|
||||
$this->processNodeForArray($child, $parts);
|
||||
}
|
||||
}
|
||||
$this->addTextPart($parts, "\n\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($node->hasChildNodes()) {
|
||||
foreach ($node->childNodes as $child) {
|
||||
$this->processNodeForArray($child, $parts);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function addTextPart(array &$parts, string $text)
|
||||
{
|
||||
if (empty($text)) return;
|
||||
|
||||
// Si la última parte fue texto, la unimos a ella.
|
||||
if (!empty($parts) && end($parts)['type'] === 'text') {
|
||||
$parts[key($parts)]['content'] .= $text;
|
||||
} else {
|
||||
$parts[] = ['type' => 'text', 'content' => $text];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user