// Configuration et variables globales const API_BASE = ''; let socket; let currentPrompts = []; let currentStatus = {}; let bot_controller = { flux_list: [] }; // Variable globale pour la liste des flux // 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); }); socket.on('ia_generator_status_changed', function(data) { // Synchroniser l'état de l'interface web const iaGeneratorToggle = document.getElementById('iaGeneratorToggle'); const iaGeneratorStatus = document.getElementById('iaGeneratorStatus'); const chatMessageToggle = document.getElementById('chatMessageToggle'); const chatMessageStatus = document.getElementById('chatMessageStatus'); if (iaGeneratorToggle && iaGeneratorStatus) { iaGeneratorToggle.checked = data.running; iaGeneratorStatus.textContent = data.running ? 'En cours...' : 'Arrêté'; iaGeneratorStatus.className = data.running ? 'text-success' : 'text-muted'; } // Synchroniser aussi l'état de l'envoi de messages if (chatMessageToggle && chatMessageStatus) { chatMessageToggle.checked = data.running; chatMessageStatus.textContent = data.running ? 'Activé' : 'Désactivé'; chatMessageStatus.className = data.running ? 'text-success' : 'text-muted'; } console.log('IA Generator status changed:', 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(), loadUsers(), loadStatus(), loadSubtitles(), loadGenerations(), loadInteractionConfig(), refreshInteractionLog(), loadSettings(), checkAutoSubtitleStatus(), // Ajouter la vérification du statut auto checkAutoMessageStatus(), // Ajouter la vérification du statut auto messages checkChatMessageStatus(), // Ajouter la vérification du statut chat messages checkSystemStatus() // Ajouter la vérification du statut système ]); } 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 fluxList = await response.json(); bot_controller.flux_list = fluxList; // Mettre à jour la variable globale renderFluxList(fluxList); } 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(); const userId = parseInt(document.getElementById('custom-message-user').value); console.log('Debug - Message:', message); console.log('Debug - User ID:', userId); console.log('Debug - User ID type:', typeof userId); console.log('Debug - Selector value:', document.getElementById('custom-message-user').value); if (!message) { showToast('Veuillez entrer un message', 'error'); return; } // Vérifier si l'utilisateur est valide (pas NaN et dans la plage des utilisateurs) if (isNaN(userId) || userId < 0 || userId >= currentUsers.length) { showToast('Veuillez sélectionner un utilisateur valide', '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', user_id: userId }) }); 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(); const userId = parseInt(document.getElementById('custom-message-user').value); if (message === 'Aucun message en attente') { showToast('Aucun message à envoyer', 'warning'); return; } // Vérifier si l'utilisateur est valide (pas NaN et dans la plage des utilisateurs) if (isNaN(userId) || userId < 0 || userId >= currentUsers.length) { showToast('Veuillez sélectionner un utilisateur valide', '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', user_id: userId }) }); 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() { const userId = parseInt(document.getElementById('custom-message-user').value); // Vérifier si l'utilisateur est valide (pas NaN et dans la plage des utilisateurs) if (isNaN(userId) || userId < 0 || userId >= currentUsers.length) { showToast('Veuillez sélectionner un utilisateur valide', 'error'); return; } 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', user_id: userId }) }); 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'); } } function processSubtitles() { // Afficher un indicateur de chargement const button = event.target; const originalText = button.innerHTML; button.innerHTML = 'Traitement en cours...'; button.disabled = true; fetch('/api/subtitles/process', { method: 'POST', headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => { if (data.success) { showToast(data.message, 'success'); // Actualiser les sous-titres loadSubtitles(); } else { showToast('Erreur: ' + data.error, 'danger'); } }) .catch(error => { console.error('Erreur:', error); showToast('Erreur lors du traitement des sous-titres', 'danger'); }) .finally(() => { // Restaurer le bouton button.innerHTML = originalText; button.disabled = false; }); } async function clearSubtitlesHistory() { // Demander confirmation avant de nettoyer if (!confirm('Êtes-vous sûr de vouloir nettoyer l\'historique des sous-titres ? Cette action est irréversible.')) { return; } try { const response = await fetch('/api/subtitles/clear', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.success) { showToast(data.message, 'success'); // Vider l'affichage de l'historique document.getElementById('subtitles-history').innerHTML = '
Aucun sous-titre dans l\'historique
'; // Mettre à jour le dernier sous-titre document.getElementById('last-subtitle').innerHTML = 'Aucun texte détecté pour le moment'; document.getElementById('last-subtitle').className = 'alert alert-warning'; } else { showToast(data.error || 'Erreur lors du nettoyage', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors du nettoyage de l\'historique', '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); } // Mettre à jour l'état du chat updateChatState(); } // === 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); }); } // === FONCTIONS UTILITAIRES === function showAlert(message, type = 'info') { const alertDiv = document.createElement('div'); alertDiv.className = `alert alert-${type} alert-dismissible fade show`; alertDiv.innerHTML = ` ${message} `; // Ajouter l'alerte en haut de la page const container = document.querySelector('.container-fluid'); container.insertBefore(alertDiv, container.firstChild); // Supprimer automatiquement après 5 secondes setTimeout(() => { if (alertDiv.parentNode) { alertDiv.remove(); } }, 5000); } // 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'); }); // === PARAMÈTRES === async function loadSettings() { try { const response = await fetch(`${API_BASE}/api/config/settings`); const result = await response.json(); if (!result.success) return; const settings = result.settings || {}; const maxCharsEl = document.getElementById('settings-message-max-chars'); if (maxCharsEl) { maxCharsEl.value = Number.isFinite(settings.twitch_message_max_chars) ? settings.twitch_message_max_chars : 100; } } catch (e) { console.error('loadSettings error:', e); } } async function saveSettings() { try { const maxCharsEl = document.getElementById('settings-message-max-chars'); const twitch_message_max_chars = maxCharsEl ? parseInt(maxCharsEl.value || '100', 10) : 100; const response = await fetch(`${API_BASE}/api/config/settings`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ settings: { twitch_message_max_chars } }) }); const result = await response.json(); if (result.success) { showToast('Paramètres sauvegardés', 'success'); } else { showToast(result.error || 'Erreur sauvegarde paramètres', 'error'); } } catch (e) { console.error('saveSettings error:', e); showToast('Erreur sauvegarde paramètres', 'error'); } } // === NAVBAR SOMMAIRE (tabs) === function activateMainTab(tabButtonId) { try { const el = document.getElementById(tabButtonId); if (!el) return; const tab = new bootstrap.Tab(el); tab.show(); } catch (e) { console.error('activateMainTab error:', e); } } function syncNavbarSitemapActive(tabButtonId) { try { const links = document.querySelectorAll('.navbar-sitemap-link[data-main-tab]'); links.forEach(a => { const target = a.getAttribute('data-main-tab'); a.classList.toggle('active', target === tabButtonId); }); } catch (e) { console.error('syncNavbarSitemapActive error:', e); } } document.addEventListener('DOMContentLoaded', function() { // Sync initial const activeMain = document.querySelector('#mainTabs .nav-link.active'); if (activeMain && activeMain.id) syncNavbarSitemapActive(activeMain.id); // Sync on tab changes const mainTabButtons = document.querySelectorAll('#mainTabs .nav-link[data-bs-toggle="tab"]'); mainTabButtons.forEach(btn => { btn.addEventListener('shown.bs.tab', function(ev) { if (ev && ev.target && ev.target.id) syncNavbarSitemapActive(ev.target.id); }); }); }); // === INTERACTION CHAT === let interactionConfig = null; let interactionRegisteredAccounts = []; async function loadInteractionConfig() { try { const response = await fetch(`${API_BASE}/api/interaction/config`); const result = await response.json(); if (!result.success) { console.error('Erreur interaction config:', result.error); return; } interactionConfig = result.config || {}; interactionRegisteredAccounts = result.registered_accounts || []; renderInteractionConfig(); } catch (error) { console.error('Erreur lors du chargement interaction config:', error); } } function renderInteractionConfig() { if (!interactionConfig) return; const enabledEl = document.getElementById('interaction-enabled'); const modeEl = document.getElementById('interaction-mode'); const tgptEnabledEl = document.getElementById('interaction-tgpt-enabled'); const tgptPrepromptEl = document.getElementById('interaction-tgpt-preprompt'); const tgptMaxCharsEl = document.getElementById('interaction-tgpt-max-chars'); const cooldownEl = document.getElementById('interaction-cooldown'); const defaultsEl = document.getElementById('interaction-default-responses'); const accountsEl = document.getElementById('interaction-registered-accounts'); if (enabledEl) enabledEl.checked = !!interactionConfig.enabled; if (modeEl) modeEl.value = interactionConfig.mode || 'predefined'; if (tgptEnabledEl) tgptEnabledEl.checked = !!interactionConfig.tgpt_enabled; if (tgptPrepromptEl) tgptPrepromptEl.value = interactionConfig.tgpt_preprompt || ''; if (tgptMaxCharsEl) tgptMaxCharsEl.value = Number.isFinite(interactionConfig.tgpt_max_chars) ? interactionConfig.tgpt_max_chars : 100; if (cooldownEl) cooldownEl.value = Number.isFinite(interactionConfig.cooldown_seconds) ? interactionConfig.cooldown_seconds : 8; if (defaultsEl) { const defaults = Array.isArray(interactionConfig.default_responses) ? interactionConfig.default_responses : ['salut']; defaultsEl.value = defaults.join('\n'); } if (accountsEl) { if (!interactionRegisteredAccounts || interactionRegisteredAccounts.length === 0) { accountsEl.textContent = 'Aucun compte enregistré (config/user.json)'; } else { accountsEl.innerHTML = interactionRegisteredAccounts .map(a => `
@${escapeHtml(String(a))}
`) .join(''); } } renderInteractionRules(); } function renderInteractionRules() { const container = document.getElementById('interaction-rules'); if (!container) return; const rules = Array.isArray(interactionConfig.rules) ? interactionConfig.rules : []; if (rules.length === 0) { container.innerHTML = `
Aucune règle. La réponse par défaut sera utilisée.
`; return; } container.innerHTML = rules.map((r, idx) => { const id = r.id || `rule_${idx}`; return `
#${idx + 1}
`; }).join(''); } function onInteractionRuleChange(ruleId) { const rules = Array.isArray(interactionConfig.rules) ? interactionConfig.rules : []; const rule = rules.find(x => String(x.id) === String(ruleId)); if (!rule) return; const enabledEl = document.getElementById(`rule-enabled-${ruleId}`); const fromEl = document.getElementById(`rule-from-${ruleId}`); const mentionEl = document.getElementById(`rule-mention-${ruleId}`); const containsEl = document.getElementById(`rule-contains-${ruleId}`); const responseEl = document.getElementById(`rule-response-${ruleId}`); const tgptPrepromptEl = document.getElementById(`rule-tgpt-preprompt-${ruleId}`); rule.enabled = enabledEl ? enabledEl.checked : true; rule.from_username = fromEl ? fromEl.value.trim() : ''; rule.mention_account = mentionEl ? mentionEl.value.trim() : ''; rule.contains_text = containsEl ? containsEl.value.trim() : ''; rule.response_text = responseEl ? responseEl.value.trim() : ''; rule.tgpt_preprompt = tgptPrepromptEl ? tgptPrepromptEl.value : ''; } async function saveInteractionRule(ruleId) { // S'assurer que les champs du DOM sont bien remontés dans l'objet onInteractionRuleChange(ruleId); await saveInteractionConfig(); } function addInteractionRule() { if (!interactionConfig) interactionConfig = {}; if (!Array.isArray(interactionConfig.rules)) interactionConfig.rules = []; const id = `r_${Date.now()}_${Math.floor(Math.random() * 1000)}`; interactionConfig.rules.unshift({ id, enabled: true, from_username: '', mention_account: '', contains_text: '', response_text: '', tgpt_preprompt: '', }); renderInteractionRules(); } function deleteInteractionRule(ruleId) { if (!interactionConfig || !Array.isArray(interactionConfig.rules)) return; interactionConfig.rules = interactionConfig.rules.filter(r => String(r.id) !== String(ruleId)); renderInteractionRules(); } async function saveInteractionConfig() { try { const enabledEl = document.getElementById('interaction-enabled'); const modeEl = document.getElementById('interaction-mode'); const tgptEnabledEl = document.getElementById('interaction-tgpt-enabled'); const tgptPrepromptEl = document.getElementById('interaction-tgpt-preprompt'); const tgptMaxCharsEl = document.getElementById('interaction-tgpt-max-chars'); const cooldownEl = document.getElementById('interaction-cooldown'); const defaultsEl = document.getElementById('interaction-default-responses'); interactionConfig = interactionConfig || {}; interactionConfig.enabled = enabledEl ? !!enabledEl.checked : true; interactionConfig.mode = modeEl ? modeEl.value : 'predefined'; interactionConfig.tgpt_enabled = tgptEnabledEl ? !!tgptEnabledEl.checked : false; interactionConfig.tgpt_preprompt = tgptPrepromptEl ? tgptPrepromptEl.value : ''; interactionConfig.tgpt_max_chars = tgptMaxCharsEl ? parseInt(tgptMaxCharsEl.value || '100', 10) : 100; interactionConfig.cooldown_seconds = cooldownEl ? parseInt(cooldownEl.value || '8', 10) : 8; const defaults = defaultsEl ? defaultsEl.value.split('\n').map(x => x.trim()).filter(Boolean) : ['salut']; interactionConfig.default_responses = defaults.length ? defaults : ['salut']; // Nettoyage minimal des règles if (Array.isArray(interactionConfig.rules)) { interactionConfig.rules = interactionConfig.rules.map(r => ({ id: String(r.id || ''), enabled: r.enabled !== false, from_username: (r.from_username || '').trim() || null, mention_account: (r.mention_account || '').trim() || null, contains_text: (r.contains_text || '').trim() || null, response_text: (r.response_text || '').trim(), tgpt_preprompt: (r.tgpt_preprompt || '').trim() || null, })).filter(r => r.id); } else { interactionConfig.rules = []; } const response = await fetch(`${API_BASE}/api/interaction/config`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ config: interactionConfig }) }); const result = await response.json(); if (result.success) { interactionConfig = result.config; renderInteractionConfig(); showToast('Interaction chat sauvegardée', 'success'); } else { showToast(result.error || 'Erreur sauvegarde interaction chat', 'error'); } } catch (error) { console.error('Erreur save interaction:', error); showToast('Erreur sauvegarde interaction chat', 'error'); } } async function refreshInteractionConfig() { await loadInteractionConfig(); } async function refreshInteractionLog() { try { const response = await fetch(`${API_BASE}/api/interaction/log?limit=120`); const result = await response.json(); const container = document.getElementById('interaction-log'); if (!container) return; if (!result.success) { container.innerHTML = `
Erreur: ${escapeHtml(String(result.error || 'unknown'))}
`; return; } const logs = Array.isArray(result.logs) ? result.logs : []; if (logs.length === 0) { container.innerHTML = `
Aucun log
`; return; } container.innerHTML = logs.slice().reverse().map(l => { const t = escapeHtml(String(l.ts || '')); const typ = escapeHtml(String(l.type || '')); const from = escapeHtml(String(l.from || '')); const content = escapeHtml(String(l.content || '')); const response = escapeHtml(String(l.response || '')); const ruleId = l.rule_id ? `rule ${escapeHtml(String(l.rule_id))}` : ''; let body = ''; if (typ === 'responded') { body = `
@${from}${content}
${response}
`; } else if (typ === 'error') { body = `
${escapeHtml(String(l.error || 'error'))}
`; } else { body = `
${escapeHtml(JSON.stringify(l))}
`; } return `
${t}
${typ}${ruleId}
${body}
`; }).join(''); } catch (error) { console.error('Erreur logs interaction:', error); } } function escapeHtml(text) { return String(text) .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function escapeAttr(text) { return escapeHtml(text).replaceAll('`', '`'); } // === GESTION DU CHAT === // Variables globales pour le chat let chatMessages = []; let chatPaused = false; let activeFlux = null; let chatInput = null; let chatMessagesContainer = null; let chatUpdateInterval = null; // Initialisation du chat function initializeChat() { chatInput = document.getElementById('chat-input'); chatMessagesContainer = document.getElementById('chat-messages'); // Gestionnaire pour l'envoi de messages chatInput.addEventListener('keypress', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendChatMessage(); } }); // Mise à jour de l'état du chat selon les flux actifs updateChatState(); } // Mise à jour de l'état du chat function updateChatState() { const hasActiveFlux = currentStatus && currentStatus.flux_count > 0; if (hasActiveFlux) { chatInput.disabled = false; chatInput.nextElementSibling.disabled = false; chatInput.placeholder = "Tapez un message..."; // Supprimer le message de bienvenue const welcomeElement = chatMessagesContainer.querySelector('.chat-welcome'); if (welcomeElement) { welcomeElement.remove(); } // Démarrer la récupération des messages de chat startChatUpdates(); // Ajouter un message système seulement si c'est la première fois const existingSystemMessage = chatMessagesContainer.querySelector('.chat-message.system-message'); if (!existingSystemMessage) { addSystemMessage('Chat connecté au stream'); } } else { chatInput.disabled = true; chatInput.nextElementSibling.disabled = true; chatInput.placeholder = "Aucun flux actif"; // Arrêter la récupération des messages stopChatUpdates(); // Afficher le message de bienvenue if (!chatMessagesContainer.querySelector('.chat-welcome')) { chatMessagesContainer.innerHTML = `

Le chat apparaîtra ici quand un flux sera actif

`; } } } // Démarrer les mises à jour du chat function startChatUpdates() { if (chatUpdateInterval) { clearInterval(chatUpdateInterval); } // Récupérer les messages toutes les 3 secondes chatUpdateInterval = setInterval(loadChatMessages, 3000); } // Arrêter les mises à jour du chat function stopChatUpdates() { if (chatUpdateInterval) { clearInterval(chatUpdateInterval); chatUpdateInterval = null; } } // Charger les messages de chat depuis l'API async function loadChatMessages() { if (chatPaused) return; try { // Trouver le premier flux actif const activeFlux = bot_controller.flux_list.find(flux => flux.active); if (!activeFlux) return; const response = await fetch(`${API_BASE}/api/chat/${activeFlux.id}/messages`); const result = await response.json(); if (result.success && result.messages) { // Ajouter les nouveaux messages result.messages.forEach(msg => { const messageExists = chatMessages.some(existing => existing.timestamp === msg.timestamp && existing.username === msg.username && existing.content === msg.content ); if (!messageExists) { addChatMessage(msg.username, msg.content, false, false); chatMessages.push(msg); } }); } } catch (error) { console.error('Erreur lors du chargement des messages de chat:', error); } } // Ajout d'un message au chat function addChatMessage(username, message, isOwn = false, isSystem = false) { const messageElement = document.createElement('div'); messageElement.className = `chat-message ${isOwn ? 'own-message' : ''} ${isSystem ? 'system-message' : ''}`; const timestamp = new Date().toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }); if (isSystem) { messageElement.innerHTML = `
${message}
${timestamp}
`; } else { messageElement.innerHTML = `
${username}
${message}
${timestamp}
`; } chatMessagesContainer.appendChild(messageElement); // Auto-scroll vers le bas chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight; // Limiter le nombre de messages (garder les 100 derniers) const messages = chatMessagesContainer.querySelectorAll('.chat-message'); if (messages.length > 100) { messages[0].remove(); } } // Ajout d'un message système function addSystemMessage(message) { // Vérifier si le message système existe déjà const existingMessages = chatMessagesContainer.querySelectorAll('.chat-message.system-message'); for (let msg of existingMessages) { const messageText = msg.querySelector('.message-text'); if (messageText && messageText.textContent === message) { return; // Message déjà présent, ne pas l'ajouter } } addChatMessage('', message, false, true); } // Envoi d'un message via le chat async function sendChatMessage() { const message = chatInput.value.trim(); const userId = parseInt(document.getElementById('chat-user-select').value); console.log('Debug - Chat Message:', message); console.log('Debug - Chat User ID:', userId); console.log('Debug - Chat User ID type:', typeof userId); console.log('Debug - Chat Selector value:', document.getElementById('chat-user-select').value); if (!message) return; // Vérifier si l'utilisateur est valide (pas NaN et dans la plage des utilisateurs) if (isNaN(userId) || userId < 0 || userId >= currentUsers.length) { showToast('Veuillez sélectionner un utilisateur valide', 'error'); return; } // Trouver le premier flux actif const activeFlux = bot_controller.flux_list.find(flux => flux.active); if (!activeFlux) { showToast('Aucun flux actif pour envoyer le message', 'error'); return; } try { const response = await fetch(`${API_BASE}/api/chat/${activeFlux.id}/send`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message, user_id: userId }) }); const result = await response.json(); if (result.success) { // Ajouter le message au chat local addChatMessage('Vous', message, true); chatInput.value = ''; showToast('Message envoyé', 'success'); } else { showToast(result.error || 'Erreur lors de l\'envoi', 'error'); } } catch (error) { console.error('Erreur lors de l\'envoi du message:', error); showToast('Erreur lors de l\'envoi du message', 'error'); } } // Effacer le chat function clearChat() { if (confirm('Voulez-vous vraiment effacer tous les messages du chat ?')) { chatMessagesContainer.innerHTML = `

Le chat apparaîtra ici quand un flux sera actif

`; chatMessages = []; showToast('Chat effacé', 'info'); } } // Pause/Reprendre le chat function toggleChat() { chatPaused = !chatPaused; const toggleIcon = document.getElementById('chat-toggle-icon'); if (chatPaused) { toggleIcon.className = 'fas fa-play'; addSystemMessage('Chat en pause'); } else { toggleIcon.className = 'fas fa-pause'; addSystemMessage('Chat repris'); } showToast(chatPaused ? 'Chat en pause' : 'Chat repris', 'info'); } // Simulation de messages de chat (pour les tests) function simulateChatMessages() { if (chatPaused) return; const usernames = ['Viewer1', 'ChatUser', 'TwitchFan', 'StreamLover', 'Gamer123']; const messages = [ 'Salut tout le monde !', 'Super stream !', 'Comment ça va ?', 'J\'adore ce jeu', 'Merci pour le stream', 'GG !', 'Belle partie', 'Continue comme ça !', 'J\'ai hâte de voir la suite', 'Bon stream !' ]; const randomUsername = usernames[Math.floor(Math.random() * usernames.length)]; const randomMessage = messages[Math.floor(Math.random() * messages.length)]; addChatMessage(randomUsername, randomMessage); } // Mise à jour du statut avec les informations du chat 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); } // Mettre à jour l'état du chat updateChatState(); } // Initialisation du chat au chargement de la page document.addEventListener('DOMContentLoaded', function() { initializeChat(); }); // === GESTION DES UTILISATEURS === // Variables globales pour les utilisateurs let currentUsers = []; // Chargement des utilisateurs async function loadUsers() { try { const response = await fetch(`${API_BASE}/api/config/users`); currentUsers = await response.json(); renderUsers(); updateUserSelectors(); // Mettre à jour les sélecteurs après le chargement } catch (error) { console.error('Erreur lors du chargement des utilisateurs:', error); } } // Rendu de la liste des utilisateurs function renderUsers() { const container = document.getElementById('users-list'); if (currentUsers.length === 0) { container.innerHTML = `
Aucun utilisateur configuré

Ajoutez des utilisateurs pour envoyer des messages

`; return; } container.innerHTML = currentUsers.map((user, index) => `
${user.tw_acc_pseudo} ${user.charactere}
${user.tw_acc_token.substring(0, 20)}...
Actif
Bypass
`).join(''); // Mettre à jour les sélecteurs d'utilisateurs updateUserSelectors(); } async function toggleUserEnabled(userId) { try { const checkbox = document.getElementById(`user-enabled-${userId}`); if (!checkbox) return; const enabled = checkbox.checked; const response = await fetch(`${API_BASE}/api/config/users/${userId}/enabled`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled }) }); const result = await response.json(); if (result.success) { currentUsers[userId] = result.user; showToast(`Utilisateur ${enabled ? 'activé' : 'désactivé'}`, 'success'); } else { checkbox.checked = !enabled; showToast(result.error || 'Erreur lors du changement d’état', 'error'); } } catch (error) { console.error('Erreur toggleUserEnabled:', error); showToast('Erreur lors du changement d’état utilisateur', 'error'); } } async function toggleUserInteractionBypass(userId) { try { const checkbox = document.getElementById(`user-bypass-${userId}`); if (!checkbox) return; const interaction_bypass_antiloop = checkbox.checked; const response = await fetch(`${API_BASE}/api/config/users/${userId}/interaction-bypass`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ interaction_bypass_antiloop }) }); const result = await response.json(); if (result.success) { currentUsers[userId] = result.user; showToast(`Bypass anti-boucle: ${interaction_bypass_antiloop ? 'ON' : 'OFF'}`, 'success'); } else { checkbox.checked = !interaction_bypass_antiloop; showToast(result.error || 'Erreur lors du changement', 'error'); } } catch (error) { console.error('Erreur toggleUserInteractionBypass:', error); showToast('Erreur lors du changement bypass', 'error'); } } // Mettre à jour les sélecteurs d'utilisateurs dans l'interface function updateUserSelectors() { console.log('Debug - updateUserSelectors called'); console.log('Debug - currentUsers:', currentUsers); const selectors = [ 'custom-message-user', 'chat-user-select' ]; selectors.forEach(selectorId => { const selector = document.getElementById(selectorId); if (selector) { console.log(`Debug - Updating selector: ${selectorId}`); // Sauvegarder la sélection actuelle const currentValue = selector.value; console.log(`Debug - Current value: ${currentValue}`); // Vider le sélecteur avec une valeur par défaut différente selector.innerHTML = ''; // Ajouter les utilisateurs currentUsers.forEach((user, index) => { const option = document.createElement('option'); option.value = index.toString(); // Convertir en chaîne option.textContent = `${user.tw_acc_pseudo} ${user.charactere}`; selector.appendChild(option); console.log(`Debug - Added option: value=${index.toString()}, text=${user.tw_acc_pseudo} ${user.charactere}`); }); // Restaurer la sélection si elle était valide if (currentValue && currentValue !== '-1' && currentValue !== '0') { selector.value = currentValue; console.log(`Debug - Restored value: ${currentValue}`); } } else { console.log(`Debug - Selector not found: ${selectorId}`); } }); console.log('Debug - Selectors updated'); } // Ouvrir le modal pour ajouter un utilisateur function openAddUserModal() { document.getElementById('userModalTitle').textContent = 'Ajouter un Utilisateur'; document.getElementById('user-edit-id').value = ''; document.getElementById('user-pseudo').value = ''; document.getElementById('user-token').value = ''; document.getElementById('user-charactere').value = '😊'; // Ouvrir le modal const modal = new bootstrap.Modal(document.getElementById('addUserModal')); modal.show(); } // Ouvrir le modal pour modifier un utilisateur function editUser(userId) { const user = currentUsers[userId]; if (!user) return; document.getElementById('userModalTitle').textContent = 'Modifier l\'Utilisateur'; document.getElementById('user-edit-id').value = userId; document.getElementById('user-pseudo').value = user.tw_acc_pseudo; document.getElementById('user-token').value = user.tw_acc_token; document.getElementById('user-charactere').value = user.charactere; // Ouvrir le modal const modal = new bootstrap.Modal(document.getElementById('addUserModal')); modal.show(); } // Sauvegarder un utilisateur (ajout ou modification) async function saveUser() { const userId = document.getElementById('user-edit-id').value; const pseudo = document.getElementById('user-pseudo').value.trim(); const token = document.getElementById('user-token').value.trim(); const charactere = document.getElementById('user-charactere').value.trim(); if (!pseudo || !token) { showToast('Pseudo et token requis', 'error'); return; } try { const url = userId ? `${API_BASE}/api/config/users/${userId}` : `${API_BASE}/api/config/users`; const method = userId ? 'PUT' : 'POST'; const response = await fetch(url, { method: method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tw_acc_pseudo: pseudo, tw_acc_token: token, charactere: charactere }) }); const result = await response.json(); if (result.success) { showToast(userId ? 'Utilisateur modifié' : 'Utilisateur ajouté', 'success'); bootstrap.Modal.getInstance(document.getElementById('addUserModal')).hide(); await loadUsers(); updateUserSelectors(); // Mettre à jour les sélecteurs } else { showToast(result.error || 'Erreur lors de la sauvegarde', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de la sauvegarde', 'error'); } } // Supprimer un utilisateur async function deleteUser(userId) { if (!confirm('Êtes-vous sûr de vouloir supprimer cet utilisateur ?')) { return; } try { const response = await fetch(`${API_BASE}/api/config/users/${userId}`, { method: 'DELETE' }); const result = await response.json(); if (result.success) { showToast('Utilisateur supprimé', 'success'); await loadUsers(); updateUserSelectors(); // Mettre à jour les sélecteurs } else { showToast(result.error || 'Erreur lors de la suppression', 'error'); } } catch (error) { console.error('Erreur:', error); showToast('Erreur lors de la suppression', 'error'); } } // Variables globales pour la génération automatique let autoSubtitleRunning = false; // Variables globales pour l'envoi automatique de messages let autoMessageRunning = false; // Écouteurs Socket.IO pour la génération automatique socket.on('subtitle_processing_start', function(data) { console.log('Début de traitement:', data.file); showProcessingFile(data.file); }); socket.on('subtitle_processing_complete', function(data) { console.log('Traitement terminé:', data.file); hideProcessingFile(); showAlert(`Sous-titre créé pour ${data.file}: ${data.subtitle}`, 'success'); loadSubtitles(); // Actualiser les sous-titres }); socket.on('subtitle_processing_error', function(data) { console.log('Erreur de traitement:', data.file, data.error); hideProcessingFile(); showAlert(`Erreur pour ${data.file}: ${data.error}`, 'danger'); }); // Écouteurs Socket.IO pour l'envoi automatique de messages socket.on('message_sending_start', function(data) { console.log('Début d\'envoi de message:', data.message); showAlert(`Envoi en cours: ${data.message}`, 'info'); }); socket.on('message_sending_complete', function(data) { console.log('Message envoyé:', data.message); showAlert(`Message envoyé: ${data.message}`, 'success'); loadGenerations(); // Actualiser les générations }); socket.on('message_sending_error', function(data) { console.log('Erreur d\'envoi:', data.message, data.error); showAlert(`Erreur d'envoi: ${data.error}`, 'danger'); }); // Fonction pour activer/désactiver l'envoi automatique de messages async function toggleAutoMessage() { const toggle = document.getElementById('autoMessageToggle'); const statusElement = document.getElementById('autoMessageStatus'); if (toggle.checked) { // Démarrer l'envoi automatique de messages try { const response = await fetch('/api/messages/auto/start', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { autoMessageRunning = true; statusElement.textContent = 'En cours...'; statusElement.className = 'text-success'; showAlert('Envoi automatique de messages démarré', 'success'); } else { toggle.checked = false; showAlert('Erreur: ' + result.error, 'danger'); } } catch (error) { console.error('Erreur:', error); toggle.checked = false; showAlert('Erreur lors du démarrage', 'danger'); } } else { // Arrêter l'envoi automatique de messages try { const response = await fetch('/api/messages/auto/stop', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { autoMessageRunning = false; statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; showAlert('Envoi automatique de messages arrêté', 'info'); } else { toggle.checked = true; showAlert('Erreur: ' + result.error, 'danger'); } } catch (error) { console.error('Erreur:', error); toggle.checked = true; showAlert('Erreur lors de l\'arrêt', 'danger'); } } } // Fonction pour vérifier le statut de l'envoi automatique de messages au chargement async function checkAutoMessageStatus() { try { const response = await fetch('/api/messages/auto/status'); const status = await response.json(); const toggle = document.getElementById('autoMessageToggle'); const statusElement = document.getElementById('autoMessageStatus'); if (toggle && statusElement) { // Synchroniser l'état du toggle avec le serveur toggle.checked = status.running; autoMessageRunning = status.running; if (status.running) { statusElement.textContent = 'En cours...'; statusElement.className = 'text-success'; console.log('Envoi automatique de messages actif'); } else { statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; console.log('Envoi automatique de messages arrêté'); } } } catch (error) { console.error('Erreur lors de la vérification du statut:', error); // En cas d'erreur, forcer l'arrêt const toggle = document.getElementById('autoMessageToggle'); const statusElement = document.getElementById('autoMessageStatus'); if (toggle && statusElement) { toggle.checked = false; statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; autoMessageRunning = false; } } } // Fonction pour activer/désactiver la génération automatique async function toggleAutoSubtitle() { const toggle = document.getElementById('autoSubtitleToggle'); const statusElement = document.getElementById('autoSubtitleStatus'); if (toggle.checked) { // Démarrer la génération automatique try { const response = await fetch('/api/subtitles/auto/start', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { autoSubtitleRunning = true; statusElement.textContent = 'En cours...'; statusElement.className = 'text-success'; showAlert('Génération automatique démarrée', 'success'); } else { toggle.checked = false; showAlert('Erreur: ' + result.error, 'danger'); } } catch (error) { console.error('Erreur:', error); toggle.checked = false; showAlert('Erreur lors du démarrage', 'danger'); } } else { // Arrêter la génération automatique try { const response = await fetch('/api/subtitles/auto/stop', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { autoSubtitleRunning = false; statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; hideProcessingFile(); showAlert('Génération automatique arrêtée', 'info'); } else { toggle.checked = true; showAlert('Erreur: ' + result.error, 'danger'); } } catch (error) { console.error('Erreur:', error); toggle.checked = true; showAlert('Erreur lors de l\'arrêt', 'danger'); } } } // Fonction pour afficher le fichier en cours de traitement function showProcessingFile(fileName) { const processingElement = document.getElementById('processing-file'); const fileNameElement = document.getElementById('current-file-name'); if (processingElement && fileNameElement) { fileNameElement.textContent = `Traitement de: ${fileName}`; processingElement.classList.remove('d-none'); } } // Fonction pour masquer le fichier en cours de traitement function hideProcessingFile() { const processingElement = document.getElementById('processing-file'); if (processingElement) { processingElement.classList.add('d-none'); } } // Fonction pour vérifier le statut de la génération automatique au chargement async function checkAutoSubtitleStatus() { try { const response = await fetch('/api/subtitles/auto/status'); const status = await response.json(); const toggle = document.getElementById('autoSubtitleToggle'); const statusElement = document.getElementById('autoSubtitleStatus'); if (toggle && statusElement) { toggle.checked = status.running; autoSubtitleRunning = status.running; if (status.running) { statusElement.textContent = 'En cours...'; statusElement.className = 'text-success'; if (status.current_file) { showProcessingFile(status.current_file); } } else { statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; hideProcessingFile(); } } } catch (error) { console.error('Erreur lors de la vérification du statut:', error); } } // Fonction pour forcer l'arrêt de l'envoi automatique async function forceStopAutoMessage() { try { const response = await fetch('/api/messages/auto/force-stop', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { // Mettre à jour l'interface const toggle = document.getElementById('autoMessageToggle'); const statusElement = document.getElementById('autoMessageStatus'); if (toggle && statusElement) { autoMessageRunning = false; toggle.checked = false; statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; } showAlert('Arrêt forcé de l\'envoi automatique de messages', 'warning'); } else { showAlert('Erreur: ' + result.error, 'danger'); } } catch (error) { console.error('Erreur lors de l\'arrêt forcé:', error); showAlert('Erreur lors de l\'arrêt forcé', 'danger'); } } // Fonction pour forcer l'arrêt de la génération automatique de sous-titres async function forceStopAutoSubtitle() { try { const response = await fetch('/api/subtitles/auto/force-stop', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { // Mettre à jour l'interface const toggle = document.getElementById('autoSubtitleToggle'); const statusElement = document.getElementById('autoSubtitleStatus'); if (toggle && statusElement) { autoSubtitleRunning = false; toggle.checked = false; statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; hideProcessingFile(); } showAlert('Arrêt forcé de la génération automatique de sous-titres', 'warning'); } else { showAlert('Erreur: ' + result.error, 'danger'); } } catch (error) { console.error('Erreur lors de l\'arrêt forcé:', error); showAlert('Erreur lors de l\'arrêt forcé', 'danger'); } } // Fonction pour activer/désactiver l'envoi de messages dans le chat async function toggleChatMessage() { const toggle = document.getElementById('chatMessageToggle'); const statusElement = document.getElementById('chatMessageStatus'); if (toggle.checked) { // Activer l'envoi de messages try { const response = await fetch('/api/chat/messages/enable', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { statusElement.textContent = 'Activé'; statusElement.className = 'text-success'; showAlert('Envoi de messages dans le chat activé', 'success'); } else { toggle.checked = false; showAlert('Erreur: ' + result.error, 'danger'); } } catch (error) { console.error('Erreur:', error); toggle.checked = false; showAlert('Erreur lors de l\'activation', 'danger'); } } else { // Désactiver l'envoi de messages try { const response = await fetch('/api/chat/messages/disable', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { statusElement.textContent = 'Désactivé'; statusElement.className = 'text-muted'; showAlert('Envoi de messages dans le chat désactivé', 'info'); } else { toggle.checked = true; showAlert('Erreur: ' + result.error, 'danger'); } } catch (error) { console.error('Erreur:', error); toggle.checked = true; showAlert('Erreur lors de la désactivation', 'danger'); } } } // Fonction pour vérifier le statut de l'envoi de messages chat au chargement async function checkChatMessageStatus() { try { const response = await fetch('/api/chat/messages/status'); const status = await response.json(); const toggle = document.getElementById('chatMessageToggle'); const statusElement = document.getElementById('chatMessageStatus'); if (toggle && statusElement) { toggle.checked = status.enabled; if (status.enabled) { statusElement.textContent = 'Activé'; statusElement.className = 'text-success'; } else { statusElement.textContent = 'Désactivé'; statusElement.className = 'text-muted'; } } } catch (error) { console.error('Erreur lors de la vérification du statut chat:', error); // En cas d'erreur, forcer la désactivation par défaut const toggle = document.getElementById('chatMessageToggle'); const statusElement = document.getElementById('chatMessageStatus'); if (toggle && statusElement) { toggle.checked = false; statusElement.textContent = 'Désactivé'; statusElement.className = 'text-muted'; } } } // === GESTION DES COMPOSANTS SYSTÈME === // Fonction pour activer/désactiver le générateur IA async function toggleIAGenerator() { const toggle = document.getElementById('iaGeneratorToggle'); const statusElement = document.getElementById('iaGeneratorStatus'); if (toggle.checked) { // Démarrer le générateur IA try { const response = await fetch('/api/ia-generator/start', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { statusElement.textContent = 'En cours...'; statusElement.className = 'text-success'; showToast('Générateur IA démarré', 'success'); } else { toggle.checked = false; showToast('Erreur: ' + result.message, 'error'); } } catch (error) { console.error('Erreur:', error); toggle.checked = false; showToast('Erreur lors du démarrage du générateur IA', 'error'); } } else { // Arrêter le générateur IA try { const response = await fetch('/api/ia-generator/stop', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; showToast('Générateur IA arrêté', 'info'); } else { toggle.checked = true; showToast('Erreur: ' + result.message, 'error'); } } catch (error) { console.error('Erreur:', error); toggle.checked = true; showToast('Erreur lors de l\'arrêt du générateur IA', 'error'); } } } // Fonction pour activer/désactiver le contrôleur Twitch async function toggleControlTwitch() { const toggle = document.getElementById('controlTwitchToggle'); const statusElement = document.getElementById('controlTwitchStatus'); if (toggle.checked) { // Démarrer le contrôleur Twitch try { const response = await fetch('/api/control-twitch/start', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { statusElement.textContent = 'En cours...'; statusElement.className = 'text-success'; showToast('Contrôleur Twitch démarré', 'success'); } else { toggle.checked = false; showToast('Erreur: ' + result.message, 'error'); } } catch (error) { console.error('Erreur:', error); toggle.checked = false; showToast('Erreur lors du démarrage du contrôleur Twitch', 'error'); } } else { // Arrêter le contrôleur Twitch try { const response = await fetch('/api/control-twitch/stop', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { statusElement.textContent = 'Arrêté'; statusElement.className = 'text-muted'; showToast('Contrôleur Twitch arrêté', 'info'); } else { toggle.checked = true; showToast('Erreur: ' + result.message, 'error'); } } catch (error) { console.error('Erreur:', error); toggle.checked = true; showToast('Erreur lors de l\'arrêt du contrôleur Twitch', 'error'); } } } // Fonction pour vérifier le statut système au chargement async function checkSystemStatus() { try { const response = await fetch('/api/system-status'); const status = await response.json(); // Mettre à jour le statut du générateur IA const iaGeneratorToggle = document.getElementById('iaGeneratorToggle'); const iaGeneratorStatus = document.getElementById('iaGeneratorStatus'); if (iaGeneratorToggle && iaGeneratorStatus) { iaGeneratorToggle.checked = status.ia_generator.running; iaGeneratorStatus.textContent = status.ia_generator.status; iaGeneratorStatus.className = status.ia_generator.running ? 'text-success' : 'text-muted'; } // Mettre à jour le statut du contrôleur Twitch const controlTwitchToggle = document.getElementById('controlTwitchToggle'); const controlTwitchStatus = document.getElementById('controlTwitchStatus'); if (controlTwitchToggle && controlTwitchStatus) { controlTwitchToggle.checked = status.control_twitch.running; controlTwitchStatus.textContent = status.control_twitch.status; controlTwitchStatus.className = status.control_twitch.running ? 'text-success' : 'text-muted'; } console.log('Statut système chargé:', status); } catch (error) { console.error('Erreur lors de la vérification du statut système:', error); } }