/* OGame Spy Auto-Importer — popup
* Impostazioni piattaforma (IP/porta), toggle cattura/notifiche,
* verifica connessione e import manuale di fallback.
*/
'use strict';
const SETTINGS_KEY = 'settings';
const DEFAULTS = { host: 'localhost', port: 8899, autoImport: true, notifications: true };
// Forme accettate per l'import manuale (come lato server)
const TOKEN_RE = /^(cr|sr|rr|mr)-[a-z]{2}-\d{1,3}-[0-9a-f]{40}$/i;
const RID_ONLY_RE = /^[0-9a-f]{40}$/i;
const refs = {
host: document.getElementById('host'),
port: document.getElementById('port'),
targetUrl: document.getElementById('targetUrl'),
btnCheck: document.getElementById('btnCheck'),
statusDot: document.getElementById('statusDot'),
statusText: document.getElementById('statusText'),
autoImport: document.getElementById('autoImport'),
notifications: document.getElementById('notifications'),
btnImport: document.getElementById('btnImport'),
importResult: document.getElementById('importResult'),
lastImport: document.getElementById('lastImport'),
};
// ---------------------------------------------------------------- utils
function loadSettings() {
return new Promise((resolve) => {
chrome.storage.local.get({ [SETTINGS_KEY]: DEFAULTS }, (d) => resolve({ ...DEFAULTS, ...(d[SETTINGS_KEY] || {}) }));
});
}
function saveSettings(cfg) {
return new Promise((resolve) => {
chrome.storage.local.set({ [SETTINGS_KEY]: cfg }, () => resolve());
});
}
function baseUrl(host, port) {
return `http://${String(host || '').trim() || 'localhost'}:${port || 8899}`;
}
function setStatus(cls, html) {
refs.statusDot.className = 'dot ' + cls;
refs.statusText.innerHTML = html;
}
function renderLast(li) {
if (!li) {
refs.lastImport.textContent = 'Nessun import effettuato.';
return;
}
if (li.ok) {
refs.lastImport.innerHTML = `✓ ${esc(li.message)}`;
} else {
refs.lastImport.innerHTML = `✗ ${esc(li.error)}`;
}
}
function esc(s) {
const d = document.createElement('div');
d.textContent = String(s == null ? '' : s);
return d.innerHTML;
}
function updateTarget() {
refs.targetUrl.textContent = `${baseUrl(refs.host.value, refs.port.value)}/api/reports`;
}
// ---------------------------------------------------------------- server check
async function checkServer() {
const host = refs.host.value.trim();
const port = refs.port.value.trim();
setStatus('wait', 'Verifica in corso…');
try {
const res = await fetch(`${baseUrl(host, port)}/api/bootstrap`, {
signal: AbortSignal.timeout(5000),
});
const data = await res.json().catch(() => null);
if (res.ok && data && data.ok) {
const s = data.server || {};
const universe = s.number ? `s${s.number}-${s.community || ''}` : (s.domain || '');
setStatus('ok',
`Connesso — ${esc(s.name || 'piattaforma')}` +
(universe ? `${esc(universe)} · ${esc(s.galaxies || '?')} galassie` : ''));
return true;
}
setStatus('err', `Risposta del server: ${esc((data && data.error) || 'HTTP ' + res.status)}`);
return false;
} catch {
setStatus('err', `Server non raggiungibile su ${esc(baseUrl(host, port))}.
Controlla IP/porta e che la piattaforma sia avviata.`);
return false;
}
}
// ---------------------------------------------------------------- import dalla clipboard
async function importFromClipboard() {
let text;
try {
text = await navigator.clipboard.readText();
} catch {
refs.importResult.className = 'result err';
refs.importResult.textContent = '✗ Impossibile leggere la clipboard.';
return;
}
const token = text.trim();
refs.importResult.textContent = '';
if (!token) {
refs.importResult.className = 'result err';
refs.importResult.textContent = 'La clipboard è vuota. Copia un API string da OGame prima.';
return;
}
if (!TOKEN_RE.test(token) && !RID_ONLY_RE.test(token)) {
refs.importResult.className = 'result err';
refs.importResult.textContent = 'Formato non riconosciuto. Atteso: sr-ar-170-<40 hex> o solo l\'id.';
return;
}
refs.btnImport.disabled = true;
refs.btnImport.textContent = 'Importazione…';
try {
const r = await chrome.runtime.sendMessage({ type: 'importManual', token });
if (r && r.ok) {
refs.importResult.className = 'result ok';
refs.importResult.textContent = (r.created ? '✓ Importato — ' : '✓ Già presente — ') + (r.message || '');
} else {
refs.importResult.className = 'result err';
refs.importResult.textContent = '✗ ' + ((r && r.error) || 'Errore sconosciuto');
}
} catch {
refs.importResult.className = 'result err';
refs.importResult.textContent = '✗ Impossibile contattare il background. Ricarica l\'estensione.';
} finally {
refs.btnImport.disabled = false;
refs.btnImport.textContent = 'Importa ora';
}
}
// ---------------------------------------------------------------- init
async function init() {
const cfg = await loadSettings();
refs.host.value = cfg.host;
refs.port.value = cfg.port;
refs.autoImport.checked = cfg.autoImport !== false;
refs.notifications.checked = cfg.notifications !== false;
updateTarget();
// ultimo esito import (dal background)
try {
const st = await chrome.runtime.sendMessage({ type: 'getState' });
if (st && st.ok) renderLast(st.lastImport);
} catch { /* background non attivo: ignora */ }
// nessuna verifica automatica: la connessione si controlla solo col
// pulsante "Verifica connessione"
setStatus('', 'Premi “Verifica connessione” per testare il server');
}
// ---------------------------------------------------------------- eventi
function onHostPortChanged() {
updateTarget();
// solo il pulsante "Verifica" interroga il server
setStatus('', 'Impostazioni salvate — premi “Verifica connessione”');
}
refs.host.addEventListener('change', async () => {
const cfg = await loadSettings();
await saveSettings({ ...cfg, host: refs.host.value.trim() || DEFAULTS.host });
onHostPortChanged();
});
refs.port.addEventListener('change', async () => {
const cfg = await loadSettings();
const p = parseInt(refs.port.value, 10);
refs.port.value = (p >= 1 && p <= 65535) ? p : DEFAULTS.port;
await saveSettings({ ...cfg, port: refs.port.value });
onHostPortChanged();
});
refs.autoImport.addEventListener('change', async () => {
const cfg = await loadSettings();
await saveSettings({ ...cfg, autoImport: refs.autoImport.checked });
});
refs.notifications.addEventListener('change', async () => {
const cfg = await loadSettings();
await saveSettings({ ...cfg, notifications: refs.notifications.checked });
});
refs.btnCheck.addEventListener('click', checkServer);
refs.btnImport.addEventListener('click', importFromClipboard);
// aggiornamento live mentre il popup è aperto (es. import automatico in corso)
chrome.storage.onChanged.addListener((changes, area) => {
if (area !== 'local' || !changes.lastImport) return;
renderLast(changes.lastImport.newValue);
});
init();