- handleVoice() downloads Telegram audio and transcribes via Whisper API - transcribeAudio() calls Whisper with Basic Auth, handles json response - TelegramWebhookController now routes message.voice and message.audio - Whisper URL, token and toggle configurable from admin config panel - Transcribed text echoed back to user then processed as normal text intent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
195 lines
12 KiB
PHP
195 lines
12 KiB
PHP
<div class="max-w-2xl mx-auto space-y-6">
|
||
|
||
<div>
|
||
<h2 class="text-xl font-bold text-gray-800">Configuración del Chat</h2>
|
||
<p class="text-gray-500 text-sm">Parámetros generales del chatbot y del canal Telegram.</p>
|
||
</div>
|
||
|
||
{{-- Formulario --}}
|
||
<div class="bg-white rounded-2xl shadow border border-gray-100 p-6 space-y-5">
|
||
|
||
{{-- Token Telegram --}}
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Token del bot de Telegram</label>
|
||
<p class="text-xs text-gray-400 mb-2">Obtenlo hablando con @BotFather en Telegram. Deja vacío si no usas Telegram.</p>
|
||
<input wire:model.defer="telegram_token" type="text"
|
||
placeholder="1234567890:ABCDefGhijKlmNoPqrStUVwxYZ"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none font-mono">
|
||
</div>
|
||
|
||
{{-- Webhook URL (solo lectura) --}}
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">URL del Webhook Telegram</label>
|
||
<p class="text-xs text-gray-400 mb-2">Esta es la URL que Telegram usará para enviarte los mensajes entrantes.</p>
|
||
<div class="flex gap-2">
|
||
<input type="text" readonly value="{{ url('/chat/webhook/telegram') }}"
|
||
class="flex-1 border border-gray-200 rounded-lg px-3 py-2 text-sm bg-gray-50 text-gray-600 font-mono">
|
||
<button wire:click="registrarWebhook"
|
||
class="flex-shrink-0 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-semibold rounded-lg transition">
|
||
<span wire:loading.remove wire:target="registrarWebhook">Registrar Webhook</span>
|
||
<span wire:loading wire:target="registrarWebhook">Registrando...</span>
|
||
</button>
|
||
</div>
|
||
@if($webhookResult)
|
||
<p class="mt-2 text-sm {{ str_contains($webhookResult, '✅') ? 'text-emerald-600' : 'text-red-600' }}">
|
||
{{ $webhookResult }}
|
||
</p>
|
||
@endif
|
||
</div>
|
||
|
||
<hr class="border-gray-100">
|
||
|
||
{{-- Mensaje de bienvenida --}}
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Mensaje de bienvenida <span class="text-red-500">*</span></label>
|
||
<p class="text-xs text-gray-400 mb-2">Primer mensaje cuando el bot no tiene menú configurado o cuando el usuario escribe "hola".</p>
|
||
<textarea wire:model.defer="mensaje_bienvenida" rows="3"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none resize-none"></textarea>
|
||
@error('mensaje_bienvenida') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
|
||
</div>
|
||
|
||
{{-- Mensaje de transferencia --}}
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Mensaje al transferir a agente <span class="text-red-500">*</span></label>
|
||
<p class="text-xs text-gray-400 mb-2">Se envía al usuario cuando elige la opción de hablar con un agente.</p>
|
||
<textarea wire:model.defer="mensaje_transferencia" rows="3"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none resize-none"></textarea>
|
||
@error('mensaje_transferencia') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
|
||
</div>
|
||
|
||
<hr class="border-gray-100">
|
||
|
||
{{-- Gemini IA --}}
|
||
<div>
|
||
<h3 class="text-sm font-bold text-gray-700 mb-3">Inteligencia Artificial (Gemini)</h3>
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">API Key de Gemini</label>
|
||
<p class="text-xs text-gray-400 mb-2">Obtén tu clave en aistudio.google.com</p>
|
||
<input wire:model.defer="gemini_api_key" type="password" placeholder="AIza..."
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none font-mono">
|
||
</div>
|
||
<div class="flex items-center gap-3">
|
||
<input wire:model.defer="gemini_habilitado" type="checkbox" value="1"
|
||
id="gemini_hab" class="w-4 h-4 accent-emerald-600">
|
||
<label for="gemini_hab" class="text-sm text-gray-700">
|
||
Habilitar IA para interpretar texto libre del usuario
|
||
</label>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Contexto extra para la IA (opcional)</label>
|
||
<textarea wire:model.defer="gemini_prompt_extra" rows="2"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none resize-none"
|
||
placeholder="Ej: Solo vendemos streaming. No ofrecemos juegos."></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="border-gray-100">
|
||
|
||
{{-- Whisper STT --}}
|
||
<div>
|
||
<h3 class="text-sm font-bold text-gray-700 mb-3">Transcripción de voz (Whisper)</h3>
|
||
<p class="text-xs text-gray-400 mb-4">Cuando un usuario envíe un audio en Telegram, se transcribirá con Whisper y el texto pasará al sistema de intención como si lo hubiera escrito.</p>
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">URL del servidor Whisper</label>
|
||
<input wire:model.defer="whisper_url" type="text" placeholder="https://whisper.u-s.app/asr"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none font-mono">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Token (contraseña Basic Auth)</label>
|
||
<input wire:model.defer="whisper_token" type="password" placeholder="Token del servidor Whisper"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none font-mono">
|
||
</div>
|
||
<div class="flex items-center gap-3">
|
||
<input wire:model.defer="whisper_habilitado" type="checkbox" value="1"
|
||
id="whisper_hab" class="w-4 h-4 accent-emerald-600">
|
||
<label for="whisper_hab" class="text-sm text-gray-700">
|
||
Habilitar transcripción de audios en Telegram
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="border-gray-100">
|
||
|
||
{{-- Validacion de pagos --}}
|
||
<div>
|
||
<h3 class="text-sm font-bold text-gray-700 mb-1">Validacion de pagos por foto + correo IMAP</h3>
|
||
<p class="text-xs text-gray-400 mb-4">La IA lee el comprobante y cruza los datos con los correos del IMAP configurado en la seccion WhatsApp.</p>
|
||
<div class="space-y-4">
|
||
<div class="flex items-center gap-3">
|
||
<input wire:model.defer="validacion_foto_habilitada" type="checkbox" value="1"
|
||
id="val_foto" class="w-4 h-4 accent-emerald-600">
|
||
<label for="val_foto" class="text-sm text-gray-700">Habilitar validacion de comprobante por foto</label>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Accion al confirmar pago</label>
|
||
<select wire:model.defer="validacion_accion_auto"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none">
|
||
<option value="solo_notificar">Solo notificar al asesor (manual)</option>
|
||
<option value="recargar_saldo">Acreditar saldo automaticamente</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Tolerancia de monto ($)</label>
|
||
<input wire:model.defer="validacion_monto_tolerancia" type="number" min="0"
|
||
class="w-32 border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none">
|
||
<p class="text-xs text-gray-400 mt-1">Diferencia maxima permitida entre comprobante y correo. Recomendado: 0.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="border-gray-100">
|
||
|
||
{{-- Bre-B --}}
|
||
<div>
|
||
<h3 class="text-sm font-bold text-gray-700 mb-1">Pago por Bre-B</h3>
|
||
<p class="text-xs text-gray-400 mb-4">Datos que se muestran al cliente cuando elige pagar por Bre-B. Deja la llave vacía para deshabilitar esta opción.</p>
|
||
<div class="space-y-4">
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Banco / Billetera</label>
|
||
<input wire:model.defer="recarga_banco_nombre" type="text" placeholder="Ej: Bancolombia, Nequi, Daviplata"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none">
|
||
@error('recarga_banco_nombre') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Llave Bre-B</label>
|
||
<input wire:model.defer="recarga_banco_llave" type="text" placeholder="Ej: 3001234567"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none font-mono">
|
||
@error('recarga_banco_llave') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-semibold text-gray-700 mb-1">Titular de la cuenta</label>
|
||
<input wire:model.defer="recarga_banco_titular" type="text" placeholder="Nombre del titular"
|
||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none">
|
||
@error('recarga_banco_titular') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Guardar --}}
|
||
<div class="flex justify-end pt-2">
|
||
<button wire:click="guardar"
|
||
class="flex items-center gap-2 bg-emerald-600 hover:bg-emerald-700 text-white text-sm font-semibold px-6 py-2.5 rounded-lg transition shadow">
|
||
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||
</svg>
|
||
<span wire:loading.remove wire:target="guardar">Guardar configuracion</span>
|
||
<span wire:loading wire:target="guardar">Guardando...</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Info de canales --}}
|
||
<div class="bg-blue-50 border border-blue-100 rounded-2xl p-5">
|
||
<h3 class="font-semibold text-blue-800 text-sm mb-2">ℹ️ Canales disponibles</h3>
|
||
<ul class="text-blue-700 text-sm space-y-1">
|
||
<li><strong>Web:</strong> Los visitantes acceden en <code class="bg-blue-100 px-1 rounded">{{ url('/chat') }}</code></li>
|
||
<li><strong>Telegram:</strong> Configurar el token y registrar el webhook para recibir mensajes de Telegram.</li>
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|