// static/script.js document.addEventListener('DOMContentLoaded', () => { // --- DOM Elements --- const form = document.getElementById('campaign-form'); const startBtn = document.getElementById('start-btn'); const pauseBtn = document.getElementById('pause-btn'); const stopBtn = document.getElementById('stop-btn'); const logOutput = document.getElementById('log-output'); const trlTextarea = document.getElementById('trl'); const templateNewNationsBtn = document.getElementById('template-new-nations-btn'); const templateWaDelegatesBtn = document.getElementById('template-wa-delegates-btn'); let eventSource = null; // --- UI State Management --- function updateUI(status) { if (status === 'running') { startBtn.disabled = true; pauseBtn.disabled = false; pauseBtn.textContent = 'Pause'; stopBtn.disabled = false; setFormDisabled(true); } else if (status === 'paused') { startBtn.disabled = true; pauseBtn.disabled = false; pauseBtn.textContent = 'Resume'; stopBtn.disabled = false; setFormDisabled(true); } else { // stopped startBtn.disabled = false; pauseBtn.disabled = true; pauseBtn.textContent = 'Pause'; stopBtn.disabled = true; setFormDisabled(false); if (eventSource) { eventSource.close(); eventSource = null; } } } function setFormDisabled(disabled) { const elements = form.elements; for (let i = 0; i < elements.length; i++) { elements[i].disabled = disabled; } } // --- Log Streaming --- function connectToLogStream() { if (eventSource) { eventSource.close(); } logOutput.textContent = 'Connecting to log stream...\n'; eventSource = new EventSource('/api/logs'); eventSource.onmessage = (event) => { logOutput.textContent += event.data + '\n'; logOutput.scrollTop = logOutput.scrollHeight; }; eventSource.onerror = () => { logOutput.textContent += '--- Connection to log stream lost. Reconnecting... ---\n'; // EventSource auto-reconnects, but we log it. }; // Custom event to close the connection from the server eventSource.addEventListener('close', () => { logOutput.textContent += '--- Log stream closed by server. ---\n'; eventSource.close(); updateUI('stopped'); }); } // --- API Calls --- async function postWithFeedback(url, body) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: body ? JSON.stringify(body) : null, credentials: 'include', // <-- ADD THIS LINE }); if (!response.ok) { const error = await response.json(); alert(`Error: ${error.error || 'Unknown error'}`); return false; } return true; } catch (error) { alert(`Network error: ${error.message}`); return false; } } // --- Local Storage for Settings --- function saveSettings() { const formData = new FormData(form); const settings = {}; formData.forEach((value, key) => { if (form.elements[key].type === 'checkbox') { settings[key] = form.elements[key].checked; } else { settings[key] = value; } }); localStorage.setItem('nsrecruit_settings', JSON.stringify(settings)); } function loadSettings() { const settings = JSON.parse(localStorage.getItem('nsrecruit_settings')); if (settings) { Object.keys(settings).forEach(key => { const element = form.elements[key]; if (element) { if (element.type === 'checkbox') { element.checked = settings[key]; } else if (element.type === 'radio') { // For radio buttons, find the one with the matching value document.querySelector(`input[name="${key}"][value="${settings[key]}"]`).checked = true; } else { element.value = settings[key]; } } }); } } // --- Event Listeners --- startBtn.addEventListener('click', async () => { if (!form.reportValidity()) return; const settings = { client_key: form.elements['client_key'].value, tg_id: form.elements['tg_id'].value, secret_key: form.elements['secret_key'].value, trl: form.elements['trl'].value, tg_type: form.elements['tg_type'].value, // For radio buttons, .value gives the selected one dry_run: form.elements['dry_run'].checked, // Use .checked for booleans continuous: form.elements['continuous'].checked, // Use .checked for booleans }; const success = await postWithFeedback('/api/start', settings); if (success) { updateUI('running'); connectToLogStream(); } }); pauseBtn.addEventListener('click', async () => { const action = pauseBtn.textContent.toLowerCase(); // 'pause' or 'resume' const success = await postWithFeedback(`/api/control/${action}`); if (success) { updateUI(action === 'pause' ? 'paused' : 'running'); } }); stopBtn.addEventListener('click', async () => { if (!confirm('Are you sure you want to stop the campaign?')) return; const success = await postWithFeedback('/api/control/stop'); if (success) { updateUI('stopped'); } }); form.addEventListener('change', saveSettings); templateNewNationsBtn.addEventListener('click', () => { const template = `# Get a pool of the latest 50 new nations +new [50]; # Remove nations ending in a number (e.g., "My Nation 5") -name_regex [\\d$]; # Remove nations ending in an underscore and Roman numerals (e.g., "queen_vii") -name_regex [[\\s_][ivxlcdmIVXLCDM]+$]; `; trlTextarea.value = template; // Trigger the change event so settings are saved to localStorage trlTextarea.dispatchEvent(new Event('change', { bubbles: true })); }); templateWaDelegatesBtn.addEventListener('click', () => { const template = `# Get all World Assembly delegates +wa [delegates]; `; trlTextarea.value = template; // Trigger the change event so settings are saved to localStorage trlTextarea.dispatchEvent(new Event('change', { bubbles: true })); }); // --- Initialization --- async function initialize() { loadSettings(); try { const response = await fetch('/api/state', { credentials: 'include' }); const state = await response.json(); updateUI(state.status); if (state.status !== 'stopped') { connectToLogStream(); } } catch (error) { logOutput.textContent = 'Could not fetch initial state from server.'; } } initialize(); });