Initial commit - Last War messaging system
This commit is contained in:
414
discord/DiscordSender.php
Executable file
414
discord/DiscordSender.php
Executable file
@@ -0,0 +1,414 @@
|
||||
<?php
|
||||
|
||||
namespace Discord;
|
||||
|
||||
class DiscordSender
|
||||
{
|
||||
private string $token;
|
||||
private string $guildId;
|
||||
private string $baseUrl = 'https://discord.com/api/v10';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->token = $_ENV['DISCORD_BOT_TOKEN'] ?? getenv('DISCORD_BOT_TOKEN');
|
||||
$this->guildId = $_ENV['DISCORD_GUILD_ID'] ?? getenv('DISCORD_GUILD_ID');
|
||||
}
|
||||
|
||||
public function sendMessage(string $channelId, string $content, ?array $embed = null, ?array $buttons = null): array
|
||||
{
|
||||
$channelId = $this->resolveUserToDmChannel($channelId);
|
||||
|
||||
$data = ['content' => $content];
|
||||
|
||||
if ($embed) {
|
||||
$data['embeds'] = [$embed];
|
||||
}
|
||||
|
||||
if ($buttons) {
|
||||
// Construir componentes correctamente
|
||||
$components = [];
|
||||
|
||||
foreach ($buttons as $row) {
|
||||
if (isset($row['components']) && is_array($row['components'])) {
|
||||
$componentRow = [
|
||||
'type' => 1,
|
||||
'components' => []
|
||||
];
|
||||
|
||||
foreach ($row['components'] as $btn) {
|
||||
$componentRow['components'][] = [
|
||||
'type' => 2,
|
||||
'style' => isset($btn['style']) ? intval($btn['style']) : 1,
|
||||
'label' => $btn['label'],
|
||||
'custom_id' => $btn['custom_id']
|
||||
];
|
||||
}
|
||||
|
||||
$components[] = $componentRow;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($components)) {
|
||||
$data['components'] = $components;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->request("POST", "/channels/{$channelId}/messages", $data);
|
||||
}
|
||||
|
||||
private function cleanComponents(array $components): array
|
||||
{
|
||||
$clean = [];
|
||||
|
||||
foreach ($components as $row) {
|
||||
$cleanRow = [];
|
||||
|
||||
if (isset($row['components'])) {
|
||||
$cleanComponents = [];
|
||||
foreach ($row['components'] as $btn) {
|
||||
$cleanBtn = [
|
||||
'type' => 2,
|
||||
'style' => $btn['style'] ?? 1,
|
||||
'label' => $btn['label'],
|
||||
'custom_id' => $btn['custom_id']
|
||||
];
|
||||
$cleanComponents[] = $cleanBtn;
|
||||
}
|
||||
$cleanRow = [
|
||||
'type' => 1,
|
||||
'components' => $cleanComponents
|
||||
];
|
||||
} else {
|
||||
$cleanRow = $row;
|
||||
}
|
||||
|
||||
$clean[] = $cleanRow;
|
||||
}
|
||||
|
||||
return $clean;
|
||||
}
|
||||
|
||||
public function sendMessageWithImages(string $channelId, string $content, array $images, ?array $buttons = null): array
|
||||
{
|
||||
$channelId = $this->resolveUserToDmChannel($channelId);
|
||||
|
||||
$result = null;
|
||||
|
||||
if (!empty($images)) {
|
||||
// Verificar si las imágenes son locales o URLs
|
||||
$localImages = [];
|
||||
$remoteImages = [];
|
||||
|
||||
foreach ($images as $imageUrl) {
|
||||
if (strpos($imageUrl, 'http') === 0) {
|
||||
// Es una URL remota
|
||||
$remoteImages[] = $imageUrl;
|
||||
} elseif (file_exists($imageUrl)) {
|
||||
// Es un archivo local
|
||||
$localImages[] = $imageUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// Enviar imágenes locales como adjuntos
|
||||
if (!empty($localImages)) {
|
||||
$result = $this->sendMessageWithAttachments($channelId, $content, $localImages);
|
||||
} else {
|
||||
$result = $this->sendMessage($channelId, $content, null, $buttons);
|
||||
}
|
||||
|
||||
// Enviar imágenes remotas como embeds
|
||||
foreach ($remoteImages as $imageUrl) {
|
||||
$embed = [
|
||||
'image' => ['url' => $imageUrl]
|
||||
];
|
||||
$result = $this->sendMessage($channelId, '', $embed, $buttons);
|
||||
$buttons = null; // Solo enviar botones en el primer mensaje
|
||||
}
|
||||
} else {
|
||||
$result = $this->sendMessage($channelId, $content, null, $buttons);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar contenido con texto e imágenes en el orden correcto
|
||||
* Divide el contenido en segmentos y los envía manteniendo el orden
|
||||
*/
|
||||
public function sendContentWithOrderedImages(string $channelId, array $segments): void
|
||||
{
|
||||
$channelId = $this->resolveUserToDmChannel($channelId);
|
||||
|
||||
foreach ($segments as $segment) {
|
||||
if ($segment['type'] === 'text') {
|
||||
// Enviar texto
|
||||
if (!empty(trim($segment['content']))) {
|
||||
$this->sendMessage($channelId, $segment['content']);
|
||||
}
|
||||
} elseif ($segment['type'] === 'image') {
|
||||
// Enviar imagen
|
||||
$imagePath = $segment['src'];
|
||||
|
||||
if (strpos($imagePath, 'http') === 0) {
|
||||
// URL remota - enviar como embed
|
||||
$embed = ['image' => ['url' => $imagePath]];
|
||||
$this->sendMessage($channelId, '', $embed);
|
||||
} elseif (file_exists($imagePath)) {
|
||||
// Archivo local - enviar como adjunto
|
||||
$this->sendMessageWithAttachments($channelId, '', [$imagePath]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function sendMessageWithAttachments(string $channelId, string $content, array $files): array
|
||||
{
|
||||
$channelId = $this->resolveUserToDmChannel($channelId);
|
||||
|
||||
$url = $this->baseUrl . "/channels/{$channelId}/messages";
|
||||
|
||||
// Preparar los datos multipart
|
||||
$postData = [
|
||||
'content' => $content,
|
||||
'payload_json' => json_encode(['content' => $content])
|
||||
];
|
||||
|
||||
// Agregar archivos
|
||||
$fileIndex = 0;
|
||||
foreach ($files as $filePath) {
|
||||
if (file_exists($filePath)) {
|
||||
$postData["file{$fileIndex}"] = new \CURLFile($filePath, mime_content_type($filePath), basename($filePath));
|
||||
$fileIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bot ' . $this->token,
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$result = json_decode($response, true);
|
||||
|
||||
if ($httpCode >= 400) {
|
||||
throw new \Exception("Discord API Error: " . ($result['message'] ?? 'Unknown error'));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function editMessage(string $channelId, string $messageId, string $content, ?array $embed = null): array
|
||||
{
|
||||
$data = ['content' => $content];
|
||||
|
||||
if ($embed) {
|
||||
$data['embeds'] = [$embed];
|
||||
}
|
||||
|
||||
return $this->request("PATCH", "/channels/{$channelId}/messages/{$messageId}", $data);
|
||||
}
|
||||
|
||||
public function deleteMessage(string $channelId, string $messageId): bool
|
||||
{
|
||||
$this->request("DELETE", "/channels/{$channelId}/messages/{$messageId}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getChannel(string $channelId): array
|
||||
{
|
||||
return $this->request("GET", "/channels/{$channelId}");
|
||||
}
|
||||
|
||||
public function getGuildChannels(): array
|
||||
{
|
||||
return $this->request("GET", "/guilds/{$this->guildId}/channels");
|
||||
}
|
||||
|
||||
private function buildActionRow(array $buttons): array
|
||||
{
|
||||
$components = [];
|
||||
|
||||
foreach ($buttons as $button) {
|
||||
$component = [
|
||||
'type' => 2,
|
||||
'style' => $button['style'] ?? 1,
|
||||
'label' => $button['label'],
|
||||
'custom_id' => $button['custom_id']
|
||||
];
|
||||
|
||||
if (isset($button['url'])) {
|
||||
$component['url'] = $button['url'];
|
||||
}
|
||||
|
||||
$components[] = $component;
|
||||
}
|
||||
|
||||
return [
|
||||
[
|
||||
'type' => 1,
|
||||
'components' => $components
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
private function request(string $method, string $endpoint, ?array $data = null): array
|
||||
{
|
||||
$url = $this->baseUrl . $endpoint;
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bot ' . $this->token,
|
||||
'Content-Type: application/json'
|
||||
]);
|
||||
|
||||
if ($data) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$result = json_decode($response, true);
|
||||
|
||||
if ($httpCode >= 400) {
|
||||
throw new \Exception("Discord API Error: " . ($result['message'] ?? 'Unknown error'));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function resolveUserToDmChannel(string $userId): string
|
||||
{
|
||||
try {
|
||||
$response = $this->request("POST", "/users/{$userId}/channels", [
|
||||
'recipient_id' => $userId
|
||||
]);
|
||||
|
||||
if (isset($response['id'])) {
|
||||
return $response['id'];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
error_log("Error creating DM channel: " . $e->getMessage());
|
||||
}
|
||||
|
||||
return $userId;
|
||||
}
|
||||
|
||||
public function splitMessage(string $content, int $maxLength = 2000): array
|
||||
{
|
||||
if (strlen($content) <= $maxLength) {
|
||||
return [$content];
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
$lines = explode("\n", $content);
|
||||
$currentPart = '';
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if (strlen($currentPart . "\n" . $line) > $maxLength) {
|
||||
if (!empty($currentPart)) {
|
||||
$parts[] = $currentPart;
|
||||
$currentPart = '';
|
||||
}
|
||||
|
||||
if (strlen($line) > $maxLength) {
|
||||
$chunks = str_split($line, $maxLength);
|
||||
$parts = array_merge($parts, array_slice($chunks, 0, -1));
|
||||
$currentPart = end($chunks);
|
||||
} else {
|
||||
$currentPart = $line;
|
||||
}
|
||||
} else {
|
||||
$currentPart .= (empty($currentPart) ? '' : "\n") . $line;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($currentPart)) {
|
||||
$parts[] = $currentPart;
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsear HTML y dividirlo en segmentos manteniendo el orden
|
||||
* Retorna array de ['type' => 'text|image', 'content' => '...', 'src' => '...']
|
||||
*/
|
||||
public function parseContent(string $html): array
|
||||
{
|
||||
$segments = [];
|
||||
$currentText = '';
|
||||
|
||||
// Usar regex para encontrar todas las etiquetas <img>
|
||||
$pattern = '/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i';
|
||||
$parts = preg_split($pattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
// El array parts alterna entre: [texto, src_imagen, texto, src_imagen, texto...]
|
||||
for ($i = 0; $i < count($parts); $i++) {
|
||||
if ($i % 2 === 0) {
|
||||
// Es texto
|
||||
$text = $this->htmlToPlainText($parts[$i]);
|
||||
if (!empty(trim($text))) {
|
||||
$segments[] = [
|
||||
'type' => 'text',
|
||||
'content' => $text
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// Es una imagen (el src capturado)
|
||||
$segments[] = [
|
||||
'type' => 'image',
|
||||
'src' => $parts[$i],
|
||||
'content' => ''
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertir HTML a texto plano manteniendo saltos de línea
|
||||
*/
|
||||
private function htmlToPlainText(string $html): string
|
||||
{
|
||||
// Reemplazar <br>, <p>, etc. con saltos de línea
|
||||
$text = preg_replace('/<br\s*\/?>/i', "\n", $html);
|
||||
$text = preg_replace('/<\/p>/i', "\n", $text);
|
||||
$text = preg_replace('/<p[^>]*>/i', '', $text);
|
||||
$text = preg_replace('/<div[^>]*>/i', '', $text);
|
||||
$text = preg_replace('/<\/div>/i', "\n", $text);
|
||||
|
||||
// Eliminar otras etiquetas HTML
|
||||
$text = strip_tags($text);
|
||||
|
||||
// Decodificar entidades HTML
|
||||
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
|
||||
// Limpiar espacios múltiples y saltos de línea
|
||||
$text = preg_replace('/\n{3,}/', "\n\n", $text);
|
||||
$text = preg_replace('/[ \t]+/', ' ', $text);
|
||||
|
||||
return trim($text);
|
||||
}
|
||||
|
||||
public function extractImages(string $html): array
|
||||
{
|
||||
preg_match_all('/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $html, $matches);
|
||||
return $matches[1] ?? [];
|
||||
}
|
||||
|
||||
public function removeImages(string $html): string
|
||||
{
|
||||
return preg_replace('/<img[^>]+>/i', '', $html);
|
||||
}
|
||||
}
|
||||
101
discord/actions/DiscordActions.php
Executable file
101
discord/actions/DiscordActions.php
Executable file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Discord\Actions;
|
||||
|
||||
use Discord\DiscordSender;
|
||||
use Discord\Converters\HtmlToDiscordMarkdownConverter;
|
||||
|
||||
class DiscordActions
|
||||
{
|
||||
private DiscordSender $sender;
|
||||
private HtmlToDiscordMarkdownConverter $converter;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->sender = new DiscordSender();
|
||||
$this->converter = new HtmlToDiscordMarkdownConverter();
|
||||
}
|
||||
|
||||
public function sendTemplate(string $channelId, string $htmlContent, ?string $command = null): array
|
||||
{
|
||||
$content = $this->converter->convert($htmlContent);
|
||||
$images = $this->converter->extractImages($htmlContent);
|
||||
|
||||
$contentWithoutImages = $this->converter->removeImages($htmlContent);
|
||||
$content = $this->converter->convert($contentWithoutImages);
|
||||
|
||||
if (!empty($images)) {
|
||||
return $this->sender->sendMessageWithImages($channelId, $content, $images);
|
||||
}
|
||||
|
||||
return $this->sender->sendMessage($channelId, $content);
|
||||
}
|
||||
|
||||
public function sendScheduledMessage(string $channelId, string $htmlContent, ?array $buttons = null): array
|
||||
{
|
||||
$content = $this->converter->convert($htmlContent);
|
||||
$images = $this->converter->extractImages($htmlContent);
|
||||
|
||||
$contentWithoutImages = $this->converter->removeImages($htmlContent);
|
||||
$content = $this->converter->convert($contentWithoutImages);
|
||||
|
||||
if (!empty($images)) {
|
||||
return $this->sender->sendMessageWithImages($channelId, $content, $images);
|
||||
}
|
||||
|
||||
return $this->sender->sendMessage($channelId, $content, null, $buttons);
|
||||
}
|
||||
|
||||
public function sendWithTranslation(string $channelId, string $htmlContent, array $translations): array
|
||||
{
|
||||
$content = $this->converter->convert($htmlContent);
|
||||
|
||||
$embed = [
|
||||
'title' => '📝 Mensaje Original',
|
||||
'description' => $content,
|
||||
'color' => 3447003,
|
||||
'footer' => [
|
||||
'text' => 'Traducciones disponibles en los botones'
|
||||
]
|
||||
];
|
||||
|
||||
$buttons = [];
|
||||
foreach ($translations as $lang => $translatedText) {
|
||||
$buttons[] = [
|
||||
'label' => strtoupper($lang),
|
||||
'custom_id' => "translate_{$lang}",
|
||||
'style' => 1
|
||||
];
|
||||
}
|
||||
|
||||
return $this->sender->sendMessage($channelId, '', $embed, $buttons);
|
||||
}
|
||||
|
||||
public function translateMessage(string $channelId, string $messageId, string $originalText, string $targetLang, string $translatedText): array
|
||||
{
|
||||
$embed = [
|
||||
'title' => "🌐 Traducción ({strtoupper($targetLang)})",
|
||||
'description' => $translatedText,
|
||||
'color' => 3066993,
|
||||
'fields' => [
|
||||
[
|
||||
'name' => 'Original',
|
||||
'value' => mb_substr($originalText, 0, 1024),
|
||||
'inline' => false
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
return $this->sender->sendMessage($channelId, '', $embed);
|
||||
}
|
||||
|
||||
public function handleButtonInteraction(string $channelId, string $messageId, string $customId): array
|
||||
{
|
||||
if (str_starts_with($customId, 'translate_')) {
|
||||
$lang = str_replace('translate_', '', $customId);
|
||||
return ['action' => 'translate', 'lang' => $lang];
|
||||
}
|
||||
|
||||
return ['action' => 'unknown'];
|
||||
}
|
||||
}
|
||||
127
discord/converters/HtmlToDiscordMarkdownConverter.php
Executable file
127
discord/converters/HtmlToDiscordMarkdownConverter.php
Executable file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Discord\Converters;
|
||||
|
||||
class HtmlToDiscordMarkdownConverter
|
||||
{
|
||||
public function convert(string $html): string
|
||||
{
|
||||
$content = $html;
|
||||
|
||||
$content = $this->convertImages($content);
|
||||
$content = $this->convertBold($content);
|
||||
$content = $this->convertItalic($content);
|
||||
$content = $this->convertUnderline($content);
|
||||
$content = $this->convertStrikethrough($content);
|
||||
$content = $this->convertCode($content);
|
||||
$content = $this->convertLinks($content);
|
||||
$content = $this->convertLists($content);
|
||||
$content = $this->convertHeaders($content);
|
||||
$content = $this->convertLineBreaks($content);
|
||||
$content = $this->cleanUp($content);
|
||||
|
||||
return trim($content);
|
||||
}
|
||||
|
||||
private function convertImages(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', '($1)', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertBold(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<strong[^>]*>(.*?)<\/strong>/is', '**$1**', $content);
|
||||
$content = preg_replace('/<b[^>]*>(.*?)<\/b>/is', '**$1**', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertItalic(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<em[^>]*>(.*?)<\/em>/is', '*$1*', $content);
|
||||
$content = preg_replace('/<i[^>]*>(.*?)<\/i>/is', '*$1*', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertUnderline(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<u[^>]*>(.*?)<\/u>/is', '__$1__', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertStrikethrough(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<s[^>]*>(.*?)<\/s>/is', '~~$1~~', $content);
|
||||
$content = preg_replace('/<strike[^>]*>(.*?)<\/strike>/is', '~~$1~~', $content);
|
||||
$content = preg_replace('/<del[^>]*>(.*?)<\/del>/is', '~~$1~~', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertCode(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<code[^>]*>(.*?)<\/code>/is', '`$1`', $content);
|
||||
$content = preg_replace('/<pre[^>]*>(.*?)<\/pre>/is', "```\n$1\n```", $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertLinks(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<a[^>]+href=["\']([^"\']+)["\'][^>]*>(.*?)<\/a>/is', '[$2]($1)', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertLists(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<li[^>]*>(.*?)<\/li>/is', "\n• $1", $content);
|
||||
$content = preg_replace('/<ul[^>]*>/is', '', $content);
|
||||
$content = preg_replace('/<\/ul>/is', '', $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertHeaders(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<h1[^>]*>(.*?)<\/h1>/is', "\n\n## $1\n", $content);
|
||||
$content = preg_replace('/<h2[^>]*>(.*?)<\/h2>/is', "\n\n### $1\n", $content);
|
||||
$content = preg_replace('/<h3[^>]*>(.*?)<\/h3>/is', "\n\n#### $1\n", $content);
|
||||
$content = preg_replace('/<h4[^>]*>(.*?)<\/h4>/is', "\n\n##### $1\n", $content);
|
||||
$content = preg_replace('/<h5[^>]*>(.*?)<\/h5>/is', "\n\n###### $1\n", $content);
|
||||
$content = preg_replace('/<h6[^>]*>(.*?)<\/h6>/is', "\n\n###### $1\n", $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function convertLineBreaks(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<br\s*\/?>/i', "\n", $content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function cleanUp(string $content): string
|
||||
{
|
||||
$content = preg_replace('/<p[^>]*>(.*?)<\/p>/is', "\n$1\n", $content);
|
||||
$content = preg_replace('/<div[^>]*>(.*?)<\/div>/is', "\n$1\n", $content);
|
||||
$content = preg_replace('/<span[^>]*>(.*?)<\/span>/is', '$1', $content);
|
||||
|
||||
$content = strip_tags($content);
|
||||
|
||||
$content = preg_replace('/ /', ' ', $content);
|
||||
$content = preg_replace('/&/', '&', $content);
|
||||
$content = preg_replace('/</', '<', $content);
|
||||
$content = preg_replace('/>/', '>', $content);
|
||||
$content = preg_replace('/"/', '"', $content);
|
||||
|
||||
$content = preg_replace('/\n{3,}/', "\n\n", $content);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function extractImages(string $html): array
|
||||
{
|
||||
preg_match_all('/<img[^>]+src=["\']([^"\']+)["\'][^>]*>/i', $html, $matches);
|
||||
return $matches[1] ?? [];
|
||||
}
|
||||
|
||||
public function removeImages(string $html): string
|
||||
{
|
||||
return preg_replace('/<img[^>]+>/i', '', $html);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user