// Configuration et variables globales const API_BASE = ''; let socket; let currentPrompts = []; let currentStatus = {}; // Initialisation de l'application document.addEventListener('DOMContentLoaded', function() { initializeSocketIO(); loadInitialData(); setupEventListeners(); startPeriodicUpdates(); }); // Initialisation de Socket.IO pour les mises à jour en temps réel function initializeSocketIO() { socket = io(); socket.on('connect', function() { console.log('Connecté au serveur'); updateConnectionStatus(true); }); socket.on('disconnect', function() { console.log('Déconnecté du serveur'); updateConnectionStatus(false); }); socket.on('status_update', function(data) { updateDashboard(data); }); socket.on('new_subtitle', function(data) { addNewSubtitle(data); }); socket.on('new_generation', function(data) { updateNextMessage(data); }); } // Mise à jour du statut de connexion function updateConnectionStatus(connected) { const statusElement = document.getElementById('connection-status'); const iconElement = statusElement.previousElementSibling; if (connected) { statusElement.textContent = 'Connecté'; iconElement.className = 'fas fa-circle text-success pulse'; } else { statusElement.textContent = 'Déconnecté'; iconElement.className = 'fas fa-circle text-danger'; } } // Chargement des données initiales async function loadInitialData() { try { await Promise.all([ loadFluxList(), loadPrompts(), loadStatus(), loadSubtitles(), loadGenerations() ]); } catch (error) { console.error('Erreur lors du chargement des données:', error); showToast('Erreur lors du chargement des données', 'error'); } } // Configuration des écouteurs d'événements function setupEventListeners() { // Modal d'ajout de flux const addFluxModal = document.getElementById('addFluxModal'); addFluxModal.addEventListener('hidden.bs.modal', function() { document.getElementById('channel-name').value = ''; document.getElementById('record-audio').checked = true; }); } // Mises à jour périodiques function startPeriodicUpdates() { // Mise à jour des données toutes les 10 secondes setInterval(async () => { await loadStatus(); await loadSubtitles(); await loadGenerations(); }, 10000); } // === GESTION DES FLUX === // Chargement de la liste des flux async function loadFluxList() { try { const response = await fetch(`${API_BASE}/api/flux`); const flux = await response.json(); renderFluxList(flux); } catch (error) { console.error('Erreur lors du chargement des flux:', error); } } // Rendu de la liste des flux function renderFluxList(fluxList) { const container = document.getElementById('flux-list'); if (fluxList.length === 0) { container.innerHTML = `

Aucun flux configuré

Cliquez sur "Ajouter Flux" pour commencer

`; return; } container.innerHTML = fluxList.map(flux => `
${flux.name} ${flux.status}
${flux.record_audio ? 'Audio' : 'Pas d\'audio' } Chat ${new Date(flux.created_at).toLocaleString()} ${flux.error ? `Erreur: ${flux.error}` : ''}
`).join(''); } // Fonction pour activer/désactiver un flux async function toggleFlux(fluxId) { try { const response = await fetch(`${API_BASE}/api/flux/${fluxId}/toggle`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { showToast(`Flux ${result.active ? 'activé' : 'désactivé'}`, 'success'); await loadFluxList(); } else { showToast(result.error || 'Erreur lors du changement de statut', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors du changement de statut du flux', 'error'); } } // Ajout d'un nouveau flux async function addFlux() { const channelName = document.getElementById('channel-name').value.trim(); const recordAudio = document.getElementById('record-audio').checked; if (!channelName) { showToast('Veuillez entrer un nom de canal', 'error'); return; } try { const response = await fetch(`${API_BASE}/api/flux`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channel_name: channelName, record_audio: recordAudio }) }); const result = await response.json(); if (result.success) { showToast(`Flux ${channelName} ajouté avec succès`, 'success'); bootstrap.Modal.getInstance(document.getElementById('addFluxModal')).hide(); await loadFluxList(); } else { showToast(result.error || 'Erreur lors de l\'ajout du flux', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de l\'ajout du flux', 'error'); } } // Suppression d'un flux async function removeFlux(fluxId) { if (!confirm('Êtes-vous sûr de vouloir supprimer ce flux ?')) { return; } try { const response = await fetch(`${API_BASE}/api/flux/${fluxId}`, { method: 'DELETE' }); const result = await response.json(); if (result.success) { showToast('Flux supprimé avec succès', 'success'); await loadFluxList(); } else { showToast(result.error || 'Erreur lors de la suppression', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de la suppression du flux', 'error'); } } // === GESTION DES PROMPTS === // Chargement des prompts async function loadPrompts() { try { const response = await fetch(`${API_BASE}/api/config/prompts`); currentPrompts = await response.json(); renderPrompts(); } catch (error) { console.error('Erreur lors du chargement des prompts:', error); } } // Rendu des prompts function renderPrompts() { const container = document.getElementById('prompts-list'); container.innerHTML = currentPrompts.map((prompt, index) => `
`).join(''); } // Ajout d'un prompt function addPrompt() { currentPrompts.push('Nouveau prompt : '); renderPrompts(); // Focus sur le nouveau prompt setTimeout(() => { const newPromptTextarea = document.querySelector(`textarea[data-prompt-index="${currentPrompts.length - 1}"]`); if (newPromptTextarea) { newPromptTextarea.focus(); newPromptTextarea.select(); } }, 100); } // Suppression d'un prompt function removePrompt(index) { if (confirm('Êtes-vous sûr de vouloir supprimer ce prompt ?')) { currentPrompts.splice(index, 1); renderPrompts(); } } // Sauvegarde des prompts async function savePrompts() { // Récupérer les valeurs des textareas const textareas = document.querySelectorAll('textarea[data-prompt-index]'); const updatedPrompts = Array.from(textareas).map(textarea => textarea.value.trim()); try { const response = await fetch(`${API_BASE}/api/config/prompts`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompts: updatedPrompts }) }); const result = await response.json(); if (result.success) { currentPrompts = updatedPrompts; showToast('Prompts sauvegardés avec succès', 'success'); } else { showToast('Erreur lors de la sauvegarde', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de la sauvegarde des prompts', 'error'); } } // === GESTION DES MESSAGES === // Envoi d'un message personnalisé async function sendCustomMessage() { const message = document.getElementById('custom-message').value.trim(); if (!message) { showToast('Veuillez entrer un message', 'error'); return; } try { const response = await fetch(`${API_BASE}/api/send-message`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message, channel: 'default' }) }); const result = await response.json(); if (result.success) { showToast('Message envoyé avec succès', 'success'); document.getElementById('custom-message').value = ''; addToRecentMessages(message); } else { showToast(result.error || 'Erreur lors de l\'envoi', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de l\'envoi du message', 'error'); } } // Envoi du prochain message async function sendNextMessage() { const nextMessageElement = document.getElementById('next-message'); const message = nextMessageElement.textContent.trim(); if (message === 'Aucun message en attente') { showToast('Aucun message à envoyer', 'warning'); return; } try { const response = await fetch(`${API_BASE}/api/send-message`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message, channel: 'default' }) }); const result = await response.json(); if (result.success) { showToast('Message envoyé avec succès', 'success'); addToRecentMessages(message); nextMessageElement.textContent = 'Aucun message en attente'; } else { showToast(result.error || 'Erreur lors de l\'envoi', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de l\'envoi du message', 'error'); } } // Envoi de la dernière génération async function sendLastGeneration() { try { const response = await fetch(`${API_BASE}/api/generations`); const generations = await response.json(); if (Object.keys(generations).length === 0) { showToast('Aucune génération disponible', 'warning'); return; } const sortedKeys = Object.keys(generations).sort(); const lastGeneration = generations[sortedKeys[sortedKeys.length - 1]]; const sendResponse = await fetch(`${API_BASE}/api/send-message`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: lastGeneration, channel: 'default' }) }); const result = await sendResponse.json(); if (result.success) { showToast('Dernière génération envoyée', 'success'); addToRecentMessages(lastGeneration); } else { showToast(result.error || 'Erreur lors de l\'envoi', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de l\'envoi de la génération', 'error'); } } // Ajout d'un message aux messages récents function addToRecentMessages(message) { const container = document.getElementById('recent-messages'); const messageElement = document.createElement('div'); messageElement.className = 'message-item fade-in'; messageElement.innerHTML = `
${message}
${new Date().toLocaleTimeString()}
`; container.insertBefore(messageElement, container.firstChild); // Limiter à 10 messages récents const messages = container.children; if (messages.length > 10) { container.removeChild(messages[messages.length - 1]); } } // === GESTION DES SOUS-TITRES === // Chargement des sous-titres async function loadSubtitles() { try { const response = await fetch(`${API_BASE}/api/subtitles`); const subtitles = await response.json(); renderSubtitles(subtitles); } catch (error) { console.error('Erreur lors du chargement des sous-titres:', error); } } // Rendu des sous-titres function renderSubtitles(subtitles) { const historyContainer = document.getElementById('subtitles-history'); const lastSubtitleElement = document.getElementById('last-subtitle'); if (Object.keys(subtitles).length === 0) { lastSubtitleElement.textContent = 'Aucun texte détecté pour le moment'; historyContainer.innerHTML = '

Aucun sous-titre disponible

'; return; } const sortedKeys = Object.keys(subtitles).sort(); const lastKey = sortedKeys[sortedKeys.length - 1]; // Mettre à jour le dernier sous-titre lastSubtitleElement.textContent = subtitles[lastKey]; // Mettre à jour l'historique historyContainer.innerHTML = sortedKeys.reverse().slice(0, 10).map(key => `
${subtitles[key]}
${key}
`).join(''); } // Génération d'une réponse à partir du sous-titre async function generateFromSubtitle() { const lastSubtitleElement = document.getElementById('last-subtitle'); const text = lastSubtitleElement.textContent.trim(); if (text === 'Aucun texte détecté pour le moment') { showToast('Aucun sous-titre disponible pour la génération', 'warning'); return; } try { const response = await fetch(`${API_BASE}/api/generate-response`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: text }) }); const result = await response.json(); if (result.success) { showToast('Génération de réponse lancée', 'success'); } else { showToast(result.error || 'Erreur lors de la génération', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de la génération de réponse', 'error'); } } // === GESTION DES GÉNÉRATIONS === // Chargement des générations async function loadGenerations() { try { const response = await fetch(`${API_BASE}/api/generations`); const generations = await response.json(); if (Object.keys(generations).length > 0) { const sortedKeys = Object.keys(generations).sort(); const lastGeneration = generations[sortedKeys[sortedKeys.length - 1]]; updateNextMessage(lastGeneration); } } catch (error) { console.error('Erreur lors du chargement des générations:', error); } } // Mise à jour du prochain message function updateNextMessage(message) { const nextMessageElement = document.getElementById('next-message'); nextMessageElement.textContent = message || 'Aucun message en attente'; } // === GESTION DU STATUT === // Chargement du statut async function loadStatus() { try { const response = await fetch(`${API_BASE}/api/status`); currentStatus = await response.json(); updateDashboard(currentStatus); } catch (error) { console.error('Erreur lors du chargement du statut:', error); } } // Mise à jour du tableau de bord function updateDashboard(status) { document.getElementById('flux-count').textContent = status.flux_count || 0; document.getElementById('recording-count').textContent = status.active_recordings || 0; document.getElementById('chat-count').textContent = status.chat_connections || 0; if (status.last_subtitle) { document.getElementById('last-subtitle').textContent = status.last_subtitle; } if (status.next_message) { updateNextMessage(status.next_message); } } // === ACTIONS RAPIDES === // Génération d'une réponse async function generateResponse() { const lastSubtitleElement = document.getElementById('last-subtitle'); const text = lastSubtitleElement.textContent.trim(); if (text === 'Aucun texte détecté pour le moment') { showToast('Aucun sous-titre disponible pour la génération', 'warning'); return; } await generateFromSubtitle(); } // Actualisation des données async function refreshData() { showToast('Actualisation en cours...', 'info'); await loadInitialData(); showToast('Données actualisées', 'success'); } // === UTILITAIRES === // Affichage des notifications toast function showToast(message, type = 'info') { // Créer le conteneur de toast s'il n'existe pas let container = document.querySelector('.toast-container'); if (!container) { container = document.createElement('div'); container.className = 'toast-container'; document.body.appendChild(container); } const toastId = 'toast-' + Date.now(); const toastColors = { success: 'text-bg-success', error: 'text-bg-danger', warning: 'text-bg-warning', info: 'text-bg-info' }; const toastElement = document.createElement('div'); toastElement.id = toastId; toastElement.className = `toast ${toastColors[type] || 'text-bg-info'}`; toastElement.setAttribute('role', 'alert'); toastElement.innerHTML = `
TwitchBot
${message}
`; container.appendChild(toastElement); const toast = new bootstrap.Toast(toastElement, { autohide: true, delay: type === 'error' ? 5000 : 3000 }); toast.show(); // Supprimer l'élément après fermeture toastElement.addEventListener('hidden.bs.toast', () => { container.removeChild(toastElement); }); } // Gestion des erreurs globales window.addEventListener('error', function(e) { console.error('Erreur JavaScript:', e.error); showToast('Une erreur inattendue s\'est produite', 'error'); }); // Gestion des erreurs de promesses non capturées window.addEventListener('unhandledrejection', function(e) { console.error('Promesse rejetée:', e.reason); showToast('Erreur de communication avec le serveur', 'error'); });