Files
sirpremiumv2/app/Http/Livewire/Chat/ShowConfiguracionChat.php
LizandroandClaude Sonnet 5 38f0986aad fix: probar OCR con imagen que tiene texto real, no pixel en blanco
Un pixel 1x1 transparente siempre da "sin_texto_detectado" aunque el
servicio funcione perfecto, mostrando un falso . Ahora manda un PNG
con texto renderizado y distingue 3 casos: conectado y leyo texto (),
conectado pero no reconocio nada (⚠️, indica problema real del OCR), y
no se pudo conectar ().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 00:42:37 +00:00

269 lines
12 KiB
PHP
Executable File

<?php
namespace App\Http\Livewire\Chat;
use App\Models\ChatConfig;
use Illuminate\Support\Facades\Http;
use Jantinnerezo\LivewireAlert\LivewireAlert;
use Livewire\Component;
class ShowConfiguracionChat extends Component
{
use LivewireAlert;
// ── Chat general ──────────────────────────────────────────
public string $telegram_token = '';
public string $mensaje_bienvenida = '';
public string $mensaje_transferencia = '';
public string $webhookResult = '';
public string $whisperResult = '';
public string $geminiResult = '';
public string $ocrResult = '';
// ── Gemini IA ─────────────────────────────────────────────
public string $gemini_api_key = '';
public string $gemini_model = 'gemini-3.5-flash';
public string $gemini_habilitado = '0';
public string $gemini_prompt_extra = '';
// ── Validación de pago por foto + correo ──────────────────
public string $validacion_foto_habilitada = '0';
public string $validacion_accion_auto = 'solo_notificar';
public string $validacion_monto_tolerancia = '0';
// ── Whisper STT ───────────────────────────────────────────
public string $whisper_url = '';
public string $whisper_token = '';
public string $whisper_habilitado = '0';
// ── OCR de comprobantes (previo a Gemini) ──────────────────
public string $ocr_url = '';
public string $ocr_token = '';
public string $ocr_habilitado = '0';
// ── Bre-B ─────────────────────────────────────────────────
public string $recarga_banco_nombre = '';
public string $recarga_banco_llave = '';
public string $recarga_banco_titular = '';
public function mount(): void
{
$keys = [
'telegram_token', 'mensaje_bienvenida', 'mensaje_transferencia',
'gemini_api_key', 'gemini_model', 'gemini_habilitado', 'gemini_prompt_extra',
'validacion_foto_habilitada', 'validacion_accion_auto', 'validacion_monto_tolerancia',
'whisper_url', 'whisper_token', 'whisper_habilitado',
'ocr_url', 'ocr_token', 'ocr_habilitado',
'recarga_banco_nombre', 'recarga_banco_llave', 'recarga_banco_titular',
];
$defaults = [
'mensaje_bienvenida' => 'Hola! Bienvenido. Escribe tu consulta.',
'mensaje_transferencia' => 'Un agente se comunicara contigo en breve. Por favor espera.',
'validacion_accion_auto' => 'solo_notificar',
'validacion_monto_tolerancia' => '0',
'gemini_model' => 'gemini-3.5-flash',
];
foreach ($keys as $key) {
$this->{$key} = ChatConfig::get($key, $defaults[$key] ?? '');
}
}
public function guardar(): void
{
$this->validate([
'mensaje_bienvenida' => 'required|string|max:500',
'mensaje_transferencia' => 'required|string|max:500',
'validacion_monto_tolerancia' => 'nullable|integer|min:0',
'recarga_banco_nombre' => 'nullable|max:60',
'recarga_banco_llave' => 'nullable|max:60',
'recarga_banco_titular' => 'nullable|max:100',
]);
$keys = [
'telegram_token', 'mensaje_bienvenida', 'mensaje_transferencia',
'gemini_api_key', 'gemini_model', 'gemini_habilitado', 'gemini_prompt_extra',
'validacion_foto_habilitada', 'validacion_accion_auto', 'validacion_monto_tolerancia',
'whisper_url', 'whisper_token', 'whisper_habilitado',
'ocr_url', 'ocr_token', 'ocr_habilitado',
'recarga_banco_nombre', 'recarga_banco_llave', 'recarga_banco_titular',
];
foreach ($keys as $key) {
ChatConfig::set($key, $this->{$key});
}
$this->alert('success', 'Configuracion guardada correctamente.');
}
public function probarGemini(): void
{
$key = trim($this->gemini_api_key ?: \App\Models\ChatConfig::get('gemini_api_key'));
$model = trim($this->gemini_model ?: \App\Models\ChatConfig::get('gemini_model', 'gemini-3.5-flash'));
if (! $key) {
$this->geminiResult = '⚠️ La API Key de Gemini está vacía.';
return;
}
// Try v1 first (newer models), fall back to v1beta
$apiVersions = ['v1', 'v1beta'];
$lastError = '';
foreach ($apiVersions as $ver) {
$url = "https://generativelanguage.googleapis.com/{$ver}/models/{$model}:generateContent";
try {
$inicio = microtime(true);
$body = [
'contents' => [['parts' => [['text' => 'Responde solo "ok"']]]],
'generationConfig' => ['maxOutputTokens' => 512, 'temperature' => 0],
];
$response = Http::timeout(15)
->withHeaders(['Content-Type' => 'application/json'])
->post("{$url}?key={$key}", $body);
$ms = (int) ((microtime(true) - $inicio) * 1000);
$data = $response->json();
if ($response->successful() && ! isset($data['error'])) {
$texto = $data['candidates'][0]['content']['parts'][0]['text'] ?? '(sin texto)';
$this->geminiResult = "✅ Gemini OK — modelo: {$model} ({$ver}) — {$ms}ms — respuesta: \"{$texto}\"";
return;
}
$lastError = "[{$ver}] " . ($data['error']['message'] ?? 'HTTP ' . $response->status());
} catch (\Throwable $e) {
$lastError = "[{$ver}] " . $e->getMessage();
}
}
// Ambas versiones fallaron — listar modelos disponibles
$lista = Http::timeout(10)
->get("https://generativelanguage.googleapis.com/v1beta/models?key={$key}");
if ($lista->successful()) {
$nombres = collect($lista->json('models', []))
->filter(fn ($m) => str_contains($m['name'] ?? '', 'gemini'))
->filter(fn ($m) => in_array('generateContent', $m['supportedGenerationMethods'] ?? []))
->pluck('name')
->map(fn ($n) => str_replace('models/', '', $n))
->values()->implode(', ');
$this->geminiResult = "❌ Error: {$lastError} — Modelos con generateContent: {$nombres}";
} else {
$this->geminiResult = "❌ Error: {$lastError}";
}
}
public function resetearWhisper(): void
{
$url = trim($this->whisper_url ?: ChatConfig::get('whisper_url'));
$token = trim($this->whisper_token ?: ChatConfig::get('whisper_token'));
if (! $url) {
$this->whisperResult = '⚠️ La URL de Whisper está vacía.';
return;
}
try {
$response = Http::timeout(10)
->withBasicAuth('whisper', $token)
->get(rtrim($url, '/asr')); // ping al servidor
$this->whisperResult = $response->successful()
? '✅ Conexión con Whisper establecida correctamente.'
: '❌ Whisper respondió con error ' . $response->status() . '.';
} catch (\Throwable $e) {
$this->whisperResult = '❌ No se pudo conectar con Whisper: ' . $e->getMessage();
}
}
public function probarOcr(): void
{
$url = \App\Services\OcrExtractionService::normalizarUrl($this->ocr_url ?: ChatConfig::get('ocr_url'));
$token = trim($this->ocr_token ?: ChatConfig::get('ocr_token'));
if (! $url) {
$this->ocrResult = '⚠️ La URL del servicio OCR está vacía.';
return;
}
// PNG 240x80 con el texto "TEST OCR 123" renderizado, para que la prueba
// sea real: un pixel en blanco siempre da "sin_texto_detectado" aunque
// todo funcione bien.
$imagenPrueba = 'iVBORw0KGgoAAAANSUhEUgAAAPAAAABQCAIAAACoK28rAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB6ElEQVR4nO3c3W6iUBhA0WHS939l5sLENPxpPRCd3bXuWgSPZVc/MXGa5/kPVPx99wLgTIImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSRE0KYImRdCkCJoUQZMiaFIETYqgSfka2Xmaps3fz/O8uen+5errrbdNBwf80WIWt18c9uC+nv/699vu69vvLWNzDZxuOuUvuz67e+f7yRsf7P5wAYt9934cudN7nZv/OetlHC+JE33EyDHP88jZXfTxvaF1OuMZ7b2MHCxj8QCPX44YMTRy/KcGm97LcXxcYdy1QW8OlLeX+w+ZKa9bxma1e7MKZ7k26L3Ttj7N03TONP+yc+997zn4/uz+9sdb9REz9Bvdp9sTJ9qHE4UZ+jq/MeifXjR84eCefd/lDUGvB+hBixy/J7Uu9fhS9+DCDi5Or9cg+itcex164eEHKwcHfHINB0dbbH35OvRrnwp9yJvgPG9NSPmNMzRhgiZF0KQImhRBkyJoUgRNiqBJETQpgiZF0KQImhRBkyJoUgRNiqBJETQpgiZF0KQImhRBkyJoUgRNiqBJETQpgiZF0KQImhRBkyJoUgRNiqBJETQpgiZF0KQImpR/3UrzkDoP+sMAAAAASUVORK5CYII=';
try {
$inicio = microtime(true);
$response = Http::withToken($token)
->timeout(15)
->post("{$url}/extract", [
'image_base64' => $imagenPrueba,
'mime_type' => 'image/png',
]);
$ms = (int) ((microtime(true) - $inicio) * 1000);
$data = $response->json();
if ($data === null) {
// No respondio JSON valido: la conexion en si fallo (ruta mal, host caido, etc).
$this->ocrResult = "❌ El servicio respondió HTTP " . $response->status() . " sin JSON válido: " .
substr($response->body(), 0, 200);
} elseif ($data['success'] ?? false) {
$texto = $data['text'] ?? '';
$this->ocrResult = "✅ Servicio OCR responde OK ({$ms}ms). Texto reconocido: \"" . substr($texto, 0, 150) . "\"";
} else {
// Respondio con JSON estructurado pero success=false: SI esta conectado,
// solo que no reconocio el texto de la imagen de prueba.
$this->ocrResult = "⚠️ El servicio conectó pero no reconoció el texto de prueba (HTTP {$response->status()}): " .
($data['error'] ?? 'sin detalle');
}
} catch (\Throwable $e) {
$this->ocrResult = '❌ No se pudo conectar con el servicio OCR: ' . $e->getMessage();
}
}
public function registrarWebhook(): void
{
$token = trim($this->telegram_token);
if (! $token) {
$this->webhookResult = '⚠️ El token de Telegram está vacío.';
return;
}
ChatConfig::set('telegram_token', $token);
$webhookUrl = url('/chat/webhook/telegram');
try {
$response = Http::timeout(15)
->post("https://api.telegram.org/bot{$token}/setWebhook", [
'url' => $webhookUrl,
]);
$data = $response->json();
if (! ($data['ok'] ?? false)) {
$this->webhookResult = '❌ Error de Telegram: ' . ($data['description'] ?? 'respuesta desconocida');
return;
}
// Registrar comandos del bot (/menu, /salir, /cancelar)
app(\App\Services\TelegramBotService::class)->registrarComandos();
$this->webhookResult = '✅ Webhook registrado y comandos configurados correctamente.';
} catch (\Throwable $e) {
$this->webhookResult = '❌ No se pudo conectar con Telegram: ' . $e->getMessage();
}
}
public function render()
{
return view('livewire.chat.show-configuracion-chat');
}
}