Beide Dienste erscheinen als Karte mit Lucide-Icon im Titel (X-Kreuz, Telegram-Papierflieger) und derselben Tabellenstruktur (Benutzername, E-Mail bzw. Telefon, Status, Anfragen, Letzte Nutzung/Pruefung, Aktionen). Die nackte Konten-Zaehlung entfaellt, die X-Knoepfe ziehen in den Kartenkopf. Telegram-Zeile zeigt Konto, Telefonnummer (Monitor-Commit 5450fd2), Status und letzte Pruefung. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
200 Zeilen
7.9 KiB
JavaScript
200 Zeilen
7.9 KiB
JavaScript
/* Recherche-Zugänge: twscrape-Account-Pool (X) + Telegram-Session-Status */
|
|
"use strict";
|
|
|
|
let xScraperCache = [];
|
|
|
|
async function loadXScraperAccounts() {
|
|
setupXScraperForms();
|
|
loadTelegramStatus();
|
|
const tbody = document.getElementById("xScraperTable");
|
|
tbody.innerHTML = '<tr><td colspan="6" class="text-muted">Lade...</td></tr>';
|
|
try {
|
|
xScraperCache = await API.get("/api/x-scraper/accounts");
|
|
renderXScraperAccounts(xScraperCache || []);
|
|
} catch (err) {
|
|
tbody.innerHTML = '<tr><td colspan="6" class="text-muted">Fehler: ' + esc(err.message || "") + '</td></tr>';
|
|
}
|
|
}
|
|
|
|
// Telegram-Session-Status (vom Monitor in die geteilte DB gemeldet),
|
|
// dargestellt als Tabellenzeile im selben Format wie die X-Konten
|
|
async function loadTelegramStatus() {
|
|
const tbody = document.getElementById("telegramStatusTable");
|
|
if (!tbody) return;
|
|
try {
|
|
const st = await API.get("/api/x-scraper/telegram-status");
|
|
if (!st.available) {
|
|
tbody.innerHTML = '<tr><td colspan="6" class="text-muted">Noch keine Statusmeldung vom Monitor vorhanden. Der Monitor meldet beim Start und täglich um 04:30 Uhr.</td></tr>';
|
|
return;
|
|
}
|
|
const status = st.ok
|
|
? '<span style="color:var(--success,#2e7d32);">Aktiv</span>'
|
|
: '<span style="color:var(--danger,#c62828);">Problem</span> <span class="text-secondary" style="font-size:12px;">' + esc(st.error || "Session nicht autorisiert") + '</span>';
|
|
const aktion = st.ok
|
|
? '<span class="text-muted" title="Die Telegram-Anmeldung läuft nicht periodisch ab, hier ist normalerweise nichts zu tun.">—</span>'
|
|
: '<span class="text-secondary" style="font-size:12px;">Neu-Login nur auf dem Server möglich</span>';
|
|
tbody.innerHTML = `<tr>
|
|
<td>${esc(st.account || "—")}</td>
|
|
<td class="text-secondary">${esc(st.phone || "—")}</td>
|
|
<td>${status}</td>
|
|
<td class="text-secondary">—</td>
|
|
<td class="text-secondary">${formatDateTime(st.updated_at)}</td>
|
|
<td>${aktion}</td>
|
|
</tr>`;
|
|
} catch (err) {
|
|
tbody.innerHTML = '<tr><td colspan="6" class="text-muted">Status konnte nicht geladen werden. ' + esc(err.message || "") + '</td></tr>';
|
|
}
|
|
}
|
|
|
|
function renderXScraperAccounts(list) {
|
|
const tbody = document.getElementById("xScraperTable");
|
|
if (!list.length) {
|
|
tbody.innerHTML = '<tr><td colspan="6" class="text-muted">Keine X-Recherche-Konten. Mit „+ Konto hinzufügen" anlegen.</td></tr>';
|
|
return;
|
|
}
|
|
tbody.innerHTML = list.map((a) => {
|
|
let status;
|
|
if (!a.active) status = '<span class="text-muted">Inaktiv</span>';
|
|
else if (a.locked) status = '<span style="color:var(--warning,#b8860b);">Gesperrt</span>';
|
|
else status = '<span style="color:var(--success,#2e7d32);">Aktiv</span>';
|
|
const lastUsed = a.last_used && typeof formatDateTime === "function"
|
|
? formatDateTime(a.last_used)
|
|
: (a.last_used || "—");
|
|
const errInfo = a.error_msg
|
|
? ' <span class="info-icon" title="' + esc(a.error_msg) + '">!</span>'
|
|
: "";
|
|
const u = esc(a.username);
|
|
const toggleLabel = a.active ? "Deaktivieren" : "Aktivieren";
|
|
return '<tr>'
|
|
+ '<td><strong>' + u + '</strong>' + errInfo + '</td>'
|
|
+ '<td>' + esc(a.email || "—") + '</td>'
|
|
+ '<td>' + status + '</td>'
|
|
+ '<td>' + (a.total_requests || 0) + '</td>'
|
|
+ '<td>' + esc(lastUsed) + '</td>'
|
|
+ '<td>'
|
|
+ '<button class="btn btn-secondary btn-small" onclick="openXScraperCookiesModal(\'' + u + '\')">Cookies erneuern</button> '
|
|
+ '<button class="btn btn-secondary btn-small" onclick="toggleXScraperActive(\'' + u + '\',' + (!a.active) + ')">' + toggleLabel + '</button> '
|
|
+ '<button class="btn btn-danger btn-small" onclick="confirmDeleteXScraper(\'' + u + '\')">Entfernen</button>'
|
|
+ '</td>'
|
|
+ '</tr>';
|
|
}).join("");
|
|
}
|
|
|
|
function openXScraperAddModal() {
|
|
document.getElementById("xScraperAddError").style.display = "none";
|
|
["xsUsername", "xsPassword", "xsEmail", "xsEmailPassword", "xsCookies"].forEach((id) => {
|
|
const el = document.getElementById(id);
|
|
if (el) el.value = "";
|
|
});
|
|
openModal("modalXScraperAdd");
|
|
}
|
|
|
|
function openXScraperCookiesModal(username) {
|
|
document.getElementById("xScraperCookiesError").style.display = "none";
|
|
document.getElementById("xsCookiesUsername").value = username;
|
|
document.getElementById("xsCookiesValue").value = "";
|
|
openModal("modalXScraperCookies");
|
|
}
|
|
|
|
async function toggleXScraperActive(username, active) {
|
|
try {
|
|
await API.post("/api/x-scraper/accounts/" + encodeURIComponent(username) + "/active", { active: active });
|
|
showToast("Status geändert.", "success");
|
|
loadXScraperAccounts();
|
|
} catch (err) {
|
|
showToast(err.message || "Status konnte nicht geändert werden", "error");
|
|
}
|
|
}
|
|
|
|
function confirmDeleteXScraper(username) {
|
|
showConfirm(
|
|
"Konto entfernen",
|
|
'Soll das X-Recherche-Konto "' + username + '" entfernt werden? Der Monitor nutzt es dann nicht mehr zum Scrapen.',
|
|
async () => {
|
|
try {
|
|
await API.del("/api/x-scraper/accounts/" + encodeURIComponent(username));
|
|
showToast("Konto entfernt.", "success");
|
|
loadXScraperAccounts();
|
|
} catch (err) {
|
|
showToast(err.message || "Konto konnte nicht entfernt werden", "error");
|
|
}
|
|
},
|
|
{ danger: true }
|
|
);
|
|
}
|
|
|
|
function resetXScraperLocks() {
|
|
showConfirm(
|
|
"Sperren zurücksetzen",
|
|
"Alle temporären Sperren der X-Recherche-Konten zurücksetzen?",
|
|
async () => {
|
|
try {
|
|
await API.post("/api/x-scraper/reset-locks", {});
|
|
showToast("Sperren zurückgesetzt.", "success");
|
|
loadXScraperAccounts();
|
|
} catch (err) {
|
|
showToast(err.message || "Sperren konnten nicht zurückgesetzt werden", "error");
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
function setupXScraperForms() {
|
|
const addForm = document.getElementById("xScraperAddForm");
|
|
if (addForm && !addForm.dataset.wired) {
|
|
addForm.dataset.wired = "1";
|
|
addForm.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
const errEl = document.getElementById("xScraperAddError");
|
|
errEl.style.display = "none";
|
|
const body = {
|
|
username: document.getElementById("xsUsername").value.trim().replace(/^@/, ""),
|
|
password: document.getElementById("xsPassword").value,
|
|
email: document.getElementById("xsEmail").value.trim(),
|
|
email_password: document.getElementById("xsEmailPassword").value,
|
|
cookies: document.getElementById("xsCookies").value.trim(),
|
|
};
|
|
if (!body.username || !body.cookies) {
|
|
errEl.textContent = "Benutzername und Cookies sind erforderlich.";
|
|
errEl.style.display = "block";
|
|
return;
|
|
}
|
|
try {
|
|
await API.post("/api/x-scraper/accounts", body);
|
|
closeModal("modalXScraperAdd");
|
|
showToast("Konto angelegt.", "success");
|
|
loadXScraperAccounts();
|
|
} catch (err) {
|
|
errEl.textContent = err.message || "Anlegen fehlgeschlagen";
|
|
errEl.style.display = "block";
|
|
}
|
|
});
|
|
}
|
|
|
|
const ckForm = document.getElementById("xScraperCookiesForm");
|
|
if (ckForm && !ckForm.dataset.wired) {
|
|
ckForm.dataset.wired = "1";
|
|
ckForm.addEventListener("submit", async (e) => {
|
|
e.preventDefault();
|
|
const errEl = document.getElementById("xScraperCookiesError");
|
|
errEl.style.display = "none";
|
|
const username = document.getElementById("xsCookiesUsername").value;
|
|
const cookies = document.getElementById("xsCookiesValue").value.trim();
|
|
if (!cookies) {
|
|
errEl.textContent = "Cookies sind erforderlich.";
|
|
errEl.style.display = "block";
|
|
return;
|
|
}
|
|
try {
|
|
await API.post("/api/x-scraper/accounts/" + encodeURIComponent(username) + "/cookies", { cookies: cookies });
|
|
closeModal("modalXScraperCookies");
|
|
showToast("Cookies erneuert.", "success");
|
|
loadXScraperAccounts();
|
|
} catch (err) {
|
|
errEl.textContent = err.message || "Cookies konnten nicht erneuert werden";
|
|
errEl.style.display = "block";
|
|
}
|
|
});
|
|
}
|
|
}
|