let recognition = null; let isProcessingSpeech = false; let isPlayingAudio = false; const STOP_WORDS = ['alto', 'detente', 'permiteme', 'callate', 'silencio']; // Elementos del DOM const chatBox = document.getElementById('chatBox'); const textInput = document.getElementById('textInput'); const sendTextButton = document.getElementById('sendText'); const modoSelect = document.getElementById('modoSelect'); const modeloSelect = document.getElementById('modeloSelect'); const vozSelect = document.getElementById('vozSelect'); const configForm = document.getElementById('configForm'); const statusLabel = document.getElementById('statusLabel'); // Manejar guardado de configuración if (configForm) { configForm.addEventListener('submit', async (e) => { e.preventDefault(); const config = { GOOGLE_API_KEY: document.getElementById('googleApiKey').value, HUGGINGFACE_TOKEN: document.getElementById('huggingfaceToken').value, OPENAI_API_KEY: document.getElementById('openaiApiKey').value }; try { const response = await fetch('/guardar_config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); const data = await response.json(); if (data.success) { alert('Configuración guardada exitosamente'); } else { alert('Error al guardar la configuración: ' + data.error); } } catch (error) { alert('Error al guardar la configuración: ' + error); } }); } // Manejar cambio de modelo AI if (modeloSelect) { modeloSelect.addEventListener('change', async function() { const response = await fetch('/cambiar_modelo', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ modelo: this.value }) }); const data = await response.json(); if (!data.success) { alert('Error al cambiar el modelo: ' + data.error); } }); } // Manejar cambio de modelo de voz if (vozSelect) { vozSelect.addEventListener('change', async function() { const response = await fetch('/cambiar_voz', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ voz: this.value }) }); const data = await response.json(); if (!data.success) { alert('Error al cambiar el modelo de voz: ' + data.error); } }); } // Manejar cambio de modo modoSelect.addEventListener('change', async function() { const response = await fetch('/cambiar_modo', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ modo: this.value }) }); const data = await response.json(); if (data.success) { addMessage(data.mensaje, 'bot'); if (data.audio) { await playAudio(data.audio); } updateStatus('Esperando activación...'); } }); // Función para verificar si el texto contiene palabras de parada function containsStopWord(text) { const lowerText = text.toLowerCase(); return STOP_WORDS.some(word => lowerText.includes(word)); } // Función para reproducir audio async function playAudio(base64Audio) { try { console.log('Preparando reproducción de audio...'); isPlayingAudio = true; // Detener completamente el reconocimiento if (recognition) { recognition.stop(); recognition.abort(); await new Promise(resolve => setTimeout(resolve, 500)); } updateStatus('Reproduciendo respuesta...'); const audio = new Audio('data:audio/mp3;base64,' + base64Audio); // Permitir interrupciones durante la reproducción recognition.start(); audio.onended = () => { console.log('Audio reproducido completamente'); isPlayingAudio = false; updateStatus('Escuchando...'); }; audio.play(); } catch (error) { console.error('Error reproduciendo audio:', error); isPlayingAudio = false; updateStatus('Error al reproducir audio. Escuchando...'); if (!isProcessingSpeech) { recognition.start(); } } } // Función para agregar mensaje al chat function addMessage(text, sender) { if (!text || !sender) { console.error('Error: texto o remitente faltante', { text, sender }); return; } const chatBox = document.getElementById('chatBox'); if (!chatBox) { console.error('Error crítico: No se encontró el elemento chatBox'); return; } try { console.log('Agregando mensaje al chat:', { text, sender }); const messageDiv = document.createElement('div'); messageDiv.className = `message ${sender}-message`; const iconSpan = document.createElement('span'); iconSpan.className = 'message-icon'; iconSpan.textContent = sender === 'user' ? '👤' : '🤖'; const textSpan = document.createElement('span'); textSpan.className = 'message-text'; textSpan.textContent = text; messageDiv.appendChild(iconSpan); messageDiv.appendChild(textSpan); chatBox.appendChild(messageDiv); // Hacer scroll al último mensaje chatBox.scrollTop = chatBox.scrollHeight; console.log('Mensaje agregado exitosamente'); } catch (error) { console.error('Error al agregar mensaje:', error); } } // Agregar múltiples mensajes function addMessages(messages) { messages.forEach(msg => { addMessage(msg.text, msg.sender); }); } // Función para actualizar el estado function updateStatus(text) { const statusLabel = document.getElementById('statusLabel'); if (statusLabel) { statusLabel.textContent = text; statusLabel.style.display = 'block'; // Asegurar que sea visible console.log('Estado actualizado:', text); } else { console.error('No se encontró el elemento statusLabel'); } } // Función para procesar la respuesta del servidor async function processServerResponse(data, userText) { try { console.log('Procesando respuesta del servidor:', data); // Mostrar el mensaje del usuario addMessage(userText, 'user'); if (data.success && data.texto) { // Mostrar la respuesta del bot console.log('Mostrando respuesta del bot:', data.texto); addMessage(data.texto, 'bot'); // Reproducir el audio si existe y no se dijo una palabra de parada if (data.audio && !containsStopWord(userText)) { await playAudio(data.audio); } } else { const errorMessage = data.error || 'Error desconocido'; console.error('Error en la respuesta:', errorMessage); addMessage('Lo siento, hubo un error: ' + errorMessage, 'bot'); } } catch (error) { console.error('Error procesando respuesta:', error); addMessage('Lo siento, ocurrió un error inesperado.', 'bot'); } } // Inicializar reconocimiento de voz function initializeSpeechRecognition() { if ('webkitSpeechRecognition' in window) { recognition = new webkitSpeechRecognition(); recognition.continuous = true; // Cambiar a true para permitir interrupciones recognition.interimResults = true; recognition.lang = 'es-ES'; recognition.onstart = function() { console.log('Reconocimiento de voz iniciado'); updateStatus('Escuchando...'); }; recognition.onend = function() { console.log('Reconocimiento de voz terminado'); if (!isProcessingSpeech) { console.log('Reiniciando reconocimiento...'); updateStatus('Escuchando...'); setTimeout(() => recognition.start(), 500); } }; recognition.onerror = function(event) { console.error('Error en reconocimiento de voz:', event.error); if (!isProcessingSpeech && event.error !== 'no-speech') { setTimeout(() => recognition.start(), 1000); } }; recognition.onresult = async function(event) { let finalTranscript = ''; let interimTranscript = ''; for (let i = event.resultIndex; i < event.results.length; ++i) { const transcript = event.results[i][0].transcript; if (event.results[i].isFinal) { finalTranscript = transcript; console.log('Texto final:', finalTranscript); // Verificar si es una palabra de parada if (containsStopWord(finalTranscript)) { console.log('Palabra de parada detectada, deteniendo audio...'); const audio = document.querySelector('audio'); if (audio) { audio.pause(); audio.currentTime = 0; } isPlayingAudio = false; updateStatus('Audio detenido. Escuchando...'); continue; } if (!isProcessingSpeech) { isProcessingSpeech = true; updateStatus('Procesando...'); try { console.log('Enviando texto al servidor:', finalTranscript); const response = await fetch('/procesar_voz', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ texto: finalTranscript }) }); const data = await response.json(); console.log('Respuesta recibida:', data); await processServerResponse(data, finalTranscript); } catch (error) { console.error('Error al procesar voz:', error); updateStatus('Error de conexión. Escuchando...'); addMessage('Lo siento, hubo un error de conexión.', 'bot'); } isProcessingSpeech = false; } } else { interimTranscript = transcript; updateStatus(`Escuchando: ${interimTranscript}`); } } }; // Iniciar reconocimiento recognition.start(); updateStatus('Escuchando...'); } else { console.error('El reconocimiento de voz no está soportado en este navegador'); alert('Tu navegador no soporta el reconocimiento de voz. Por favor, usa Chrome.'); updateStatus('Reconocimiento de voz no soportado'); } } // Manejar envío de texto sendTextButton.addEventListener('click', async () => { const text = textInput.value.trim(); if (text) { updateStatus('Procesando...'); addMessage(text, 'user'); try { const response = await fetch('/procesar_voz', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ texto: text }) }); const data = await response.json(); await processServerResponse(data, text); textInput.value = ''; updateStatus('Esperando activación...'); } catch (error) { console.error('Error:', error); updateStatus('Error de conexión'); } } }); // Inicializar cuando se carga la página document.addEventListener('DOMContentLoaded', () => { console.log('Página cargada, verificando elementos...'); // Verificar que existan los elementos necesarios const chatBox = document.getElementById('chatBox'); const statusLabel = document.getElementById('statusLabel'); if (!chatBox) { console.error('Error crítico: No se encontró el elemento chatBox'); return; } if (!statusLabel) { console.error('Error crítico: No se encontró el elemento statusLabel'); return; } // Asegurar que el statusLabel sea visible statusLabel.style.display = 'block'; console.log('Elementos encontrados, inicializando reconocimiento de voz...'); initializeSpeechRecognition(); // Agregar mensaje de bienvenida addMessage('¡Hola! Soy tu asistente virtual. ¿En qué puedo ayudarte?', 'bot'); });