'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'); } }