NSrecruit / static /script.js
Bohaska
fix regex
c5b9fb3
// 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();
});