up
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Livewire\Whatsapp;
|
||||
|
||||
use App\Models\WhatsappSystemConfig;
|
||||
use App\Services\BancolombiaParser;
|
||||
use App\Services\CorreoImapService;
|
||||
use Livewire\Component;
|
||||
use Jantinnerezo\LivewireAlert\LivewireAlert;
|
||||
@@ -82,7 +83,14 @@ class ShowCorreoConfig extends Component
|
||||
timeout: 20
|
||||
);
|
||||
|
||||
$this->emails = $service->fetchLatest(5);
|
||||
$raw = $service->fetchLatest(5);
|
||||
|
||||
// Añadir datos parseados de Bancolombia a cada correo
|
||||
$this->emails = array_map(function (array $email) {
|
||||
$email['bancolombia'] = BancolombiaParser::parse($email['body']);
|
||||
return $email;
|
||||
}, $raw);
|
||||
|
||||
$this->testStatus = 'ok';
|
||||
} catch (\Throwable $e) {
|
||||
$this->testStatus = 'error';
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
/**
|
||||
* BancolombiaParser
|
||||
*
|
||||
* Extrae campos estructurados de los correos de notificación de Bancolombia.
|
||||
* Soporta notificaciones de transferencias por llave de pago.
|
||||
*
|
||||
* Ejemplo de texto:
|
||||
* "Bancolombia: LEDYS, recibiste una transferencia de HELMER DE JESUS RINCON ORTEGA
|
||||
* por $1,000.00 en tu cuenta *0733 conectada a la llave @leon8909 el 21/04/26
|
||||
* a las 19:51."
|
||||
*/
|
||||
class BancolombiaParser
|
||||
{
|
||||
/**
|
||||
* Intenta parsear el cuerpo de texto del correo.
|
||||
*
|
||||
* @return array|null Null si el texto no parece una notificación de Bancolombia.
|
||||
* Array con claves: destinatario, remitente, valor, cuenta, llave, fecha, hora, texto_original
|
||||
*/
|
||||
public static function parse(string $text): ?array
|
||||
{
|
||||
// Limpiar HTML si llega con etiquetas
|
||||
$text = self::stripHtml($text);
|
||||
|
||||
// Normalizar espacios y saltos de línea
|
||||
$text = preg_replace('/\s+/', ' ', $text);
|
||||
$text = trim($text);
|
||||
|
||||
// Detectar si es correo de Bancolombia
|
||||
if (! self::esBancolombia($text)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = [
|
||||
'banco' => 'Bancolombia',
|
||||
'tipo' => self::detectarTipo($text),
|
||||
'destinatario' => self::extraer($text, '/Bancolombia[:\s]+([A-ZÁÉÍÓÚÑÜ][A-Za-záéíóúñü\s]+?),\s+recibiste/u'),
|
||||
'remitente' => self::extraer($text, '/transferencia\s+de\s+(.+?)\s+por\s+\$/iu'),
|
||||
'valor' => self::extraerValor($text),
|
||||
'cuenta' => self::extraer($text, '/cuenta\s+\*(\d+)/iu'),
|
||||
'llave' => self::extraer($text, '/llave\s+([@\w.\-]+)/iu'),
|
||||
'fecha' => self::extraer($text, '/el\s+(\d{1,2}\/\d{1,2}\/\d{2,4})/iu'),
|
||||
'hora' => self::extraer($text, '/a\s+las\s+(\d{1,2}:\d{2})/iu'),
|
||||
'texto_original' => $text,
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────
|
||||
// Helpers privados
|
||||
// ─────────────────────────────────────────────────────────
|
||||
|
||||
private static function esBancolombia(string $text): bool
|
||||
{
|
||||
return stripos($text, 'bancolombia') !== false
|
||||
&& (
|
||||
stripos($text, 'transferencia') !== false
|
||||
|| stripos($text, 'pago') !== false
|
||||
|| stripos($text, 'recibiste') !== false
|
||||
|| stripos($text, 'enviaste') !== false
|
||||
);
|
||||
}
|
||||
|
||||
private static function detectarTipo(string $text): string
|
||||
{
|
||||
if (stripos($text, 'recibiste') !== false) {
|
||||
return 'transferencia_recibida';
|
||||
}
|
||||
if (stripos($text, 'enviaste') !== false) {
|
||||
return 'transferencia_enviada';
|
||||
}
|
||||
if (stripos($text, 'pago') !== false) {
|
||||
return 'pago';
|
||||
}
|
||||
return 'notificacion';
|
||||
}
|
||||
|
||||
/**
|
||||
* Ejecuta un regex y devuelve el primer grupo capturado, trimmed.
|
||||
*/
|
||||
private static function extraer(string $text, string $pattern): string
|
||||
{
|
||||
if (preg_match($pattern, $text, $m)) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrae el valor monetario como string limpio, ej: "1000.00"
|
||||
* y también como float.
|
||||
* Devuelve el string formateado con separadores originales.
|
||||
*/
|
||||
private static function extraerValor(string $text): string
|
||||
{
|
||||
// Captura números con puntos y comas: $1,000.00 | $1.000,00 | $500
|
||||
if (preg_match('/por\s+\$([0-9][0-9.,]*)/iu', $text, $m)) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Elimina etiquetas HTML y decodifica entidades.
|
||||
*/
|
||||
private static function stripHtml(string $text): string
|
||||
{
|
||||
// Reemplazar <br>, <p>, <div>, <tr>, <li> con salto de línea para preservar estructura
|
||||
$text = preg_replace('/<(br\s*\/?|\/p|\/div|\/tr|\/li)>/i', "\n", $text);
|
||||
$text = strip_tags($text);
|
||||
$text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
@@ -273,6 +273,14 @@ class CorreoImapService
|
||||
// Eliminar líneas de cabecera de parte MIME (Content-Type, etc.)
|
||||
$body = preg_replace('/^(Content-[^\r\n]+\r?\n)+/mi', '', $body);
|
||||
|
||||
// Si el cuerpo contiene HTML, convertir bloques estructurales a saltos
|
||||
// de línea y luego quitar todas las etiquetas para obtener texto plano
|
||||
if (preg_match('/<[a-z][\s>]/i', $body)) {
|
||||
$body = preg_replace('/<(br\s*\/?|\/p|\/div|\/tr|\/li)>/i', "\n", $body);
|
||||
$body = strip_tags($body);
|
||||
$body = html_entity_decode($body, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
}
|
||||
|
||||
// Normalizar saltos de línea
|
||||
$body = str_replace(["\r\n", "\r"], "\n", $body);
|
||||
|
||||
|
||||
@@ -190,8 +190,75 @@
|
||||
|
||||
{{-- Cuerpo del correo --}}
|
||||
<div x-show="open" x-transition class="border-t border-gray-100 px-4 py-3">
|
||||
|
||||
{{-- Tarjeta de datos Bancolombia ─────────────────── --}}
|
||||
@if(!empty($email['bancolombia']))
|
||||
@php $bc = $email['bancolombia']; @endphp
|
||||
<div class="mb-3 rounded-xl bg-gradient-to-br from-yellow-50 to-amber-50 border border-amber-200 p-4">
|
||||
<div class="flex items-center gap-2 mb-3">
|
||||
<span class="inline-flex items-center gap-1.5 bg-amber-500 text-white text-xs font-bold px-2.5 py-1 rounded-full">
|
||||
<svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Bancolombia — datos extraídos
|
||||
</span>
|
||||
@if($bc['tipo'] === 'transferencia_recibida')
|
||||
<span class="text-xs bg-green-100 text-green-700 px-2 py-0.5 rounded-full font-medium">Pago recibido</span>
|
||||
@elseif($bc['tipo'] === 'transferencia_enviada')
|
||||
<span class="text-xs bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full font-medium">Transferencia enviada</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-x-6 gap-y-2 text-sm">
|
||||
@if($bc['valor'])
|
||||
<div>
|
||||
<span class="text-xs text-gray-500 block">Valor</span>
|
||||
<span class="font-bold text-green-700 text-lg">${{ $bc['valor'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($bc['llave'])
|
||||
<div>
|
||||
<span class="text-xs text-gray-500 block">Llave recibe</span>
|
||||
<span class="font-semibold text-gray-800">{{ $bc['llave'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($bc['fecha'])
|
||||
<div>
|
||||
<span class="text-xs text-gray-500 block">Fecha</span>
|
||||
<span class="font-medium text-gray-700">{{ $bc['fecha'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($bc['hora'])
|
||||
<div>
|
||||
<span class="text-xs text-gray-500 block">Hora</span>
|
||||
<span class="font-medium text-gray-700">{{ $bc['hora'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($bc['remitente'])
|
||||
<div class="col-span-2">
|
||||
<span class="text-xs text-gray-500 block">Remitente</span>
|
||||
<span class="font-medium text-gray-700">{{ $bc['remitente'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($bc['destinatario'])
|
||||
<div class="col-span-2">
|
||||
<span class="text-xs text-gray-500 block">Destinatario</span>
|
||||
<span class="font-medium text-gray-700">{{ $bc['destinatario'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if($bc['cuenta'])
|
||||
<div class="col-span-2">
|
||||
<span class="text-xs text-gray-500 block">Cuenta</span>
|
||||
<span class="font-medium text-gray-700">*{{ $bc['cuenta'] }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Texto completo del correo ────────────────────── --}}
|
||||
@if($email['body'])
|
||||
<pre class="text-xs text-gray-600 whitespace-pre-wrap font-sans leading-relaxed max-h-64 overflow-y-auto">{{ $email['body'] }}</pre>
|
||||
<p class="text-xs text-gray-400 mb-1 font-medium">Texto completo:</p>
|
||||
<pre class="text-xs text-gray-600 whitespace-pre-wrap font-sans leading-relaxed max-h-48 overflow-y-auto">{{ $email['body'] }}</pre>
|
||||
@else
|
||||
<p class="text-xs text-gray-400 italic">(Sin contenido de texto)</p>
|
||||
@endif
|
||||
|
||||
Reference in New Issue
Block a user