mirror of
https://github.com/Ry3yr/Fake-Social-MediaWriter.git
synced 2026-08-19 17:20:03 -04:00
2755aacd2f
Show unmatched Akkoma entries
299 lines
12 KiB
HTML
299 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Alcea ↔ Akkoma Match Viewer</title>
|
|
<style>
|
|
body { font-family: 'Segoe UI', Tahoma, sans-serif; padding: 20px; max-width: 1200px; margin: 0 auto; background-color: #f8f9fa; color: #333; }
|
|
h1 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
|
|
.stats { font-weight: bold; margin: 20px 0; padding: 15px; background: #e8f4f8; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
.stat-item { display: inline-block; margin: 0 15px; cursor: pointer; padding: 8px 12px; border-radius: 4px; transition: background-color 0.2s; }
|
|
.stat-item:hover { background-color: #d1e7f5; }
|
|
.stat-count { color: #2980b9; font-weight: bold; font-size: 1.2em; }
|
|
.match { background: white; padding: 16px; margin: 16px 0; border-left: 5px solid #2ecc71; border-radius: 4px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
|
|
.url { font-family: monospace; font-size: 0.9em; color: #7f8c8d; margin: 8px 0; }
|
|
.preview { font-style: italic; color: #555; margin: 10px 0; padding: 10px; background-color: #f9f9f9; border-left: 3px solid #bdc3c7; }
|
|
pre { background: #f5f5f5; padding: 12px; border: 1px solid #ddd; overflow-x: auto; border-radius: 4px; font-size: 0.95em; }
|
|
.match-type { display: inline-block; padding: 4px 10px; border-radius: 4px; color: white; font-size: 0.8em; margin-left: 10px; background: #7f8c8d; }
|
|
.match-type.raw { background: #27ae60; }
|
|
.match-type.normalized { background: #2ecc71; }
|
|
.match-type.noBreaks { background: #3498db; }
|
|
.match-type.loose { background: #9b59b6; }
|
|
.match-type.finalNormalized { background: #e67e22; }
|
|
.filter-active { background-color: #d1e7f5; box-shadow: 0 0 0 2px #3498db inset; }
|
|
.no-matches, .loading, .error { text-align: center; padding: 30px; color: #7f8c8d; }
|
|
.error { color: #c0392b; background: #fadbd8; border-radius: 5px; margin: 20px 0; }
|
|
.refresh-button { background-color: #3498db; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; margin-left: 10px; font-size: 0.9em; }
|
|
.refresh-button:hover { background-color: #2980b9; }
|
|
.preview-link { cursor: pointer; color: #2980b9; text-decoration: underline; margin-left: 10px; display: inline-block; font-size: 0.9em; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<h1>Compare Alcea with Akkoma</h1>
|
|
<div class="stats" id="stats">Loading data...</div>
|
|
<div id="matches"></div>
|
|
|
|
<script>
|
|
let allData = {
|
|
alceaEntries: [],
|
|
akkomaEntries: [],
|
|
matches: [],
|
|
unmatchedAkkoma: []
|
|
};
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const filterType = urlParams.get('filter');
|
|
const filterValue = urlParams.get('value');
|
|
|
|
function getCacheBustingTimestamp() {
|
|
return new Date().getTime();
|
|
}
|
|
|
|
function normalizeLineBreaks(str) {
|
|
return str.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
}
|
|
|
|
function normalizeText(str) {
|
|
return normalizeLineBreaks(str).normalize('NFC').trim();
|
|
}
|
|
|
|
function removeLineBreaks(str) {
|
|
return normalizeText(str).replace(/\n/g, '');
|
|
}
|
|
|
|
function normalizeTextCompletely(str) {
|
|
return str.replace(/\r\n|\r/g, '\n').replace(/\s+/g, ' ').trim().normalize('NFC');
|
|
}
|
|
|
|
async function fetchJson(url) {
|
|
const cacheBuster = getCacheBustingTimestamp();
|
|
const urlWithCacheBusting = url.includes('?')
|
|
? `${url}&_=${cacheBuster}`
|
|
: `${url}?_=${cacheBuster}`;
|
|
|
|
const res = await fetch(urlWithCacheBusting);
|
|
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
|
|
return await res.json();
|
|
}
|
|
|
|
function extractAlceaEntries(alceaData) {
|
|
const entries = [];
|
|
if (Array.isArray(alceaData)) {
|
|
alceaData.forEach(item => {
|
|
if (typeof item === 'object' && item !== null) {
|
|
Object.values(item).forEach(entry => {
|
|
if (entry && typeof entry.value === 'string' && entry.value.includes('#acws')) {
|
|
entries.push(entry);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} else if (typeof alceaData === 'object' && alceaData !== null) {
|
|
Object.values(alceaData).forEach(entry => {
|
|
if (entry && typeof entry.value === 'string' && entry.value.includes('#acws')) {
|
|
entries.push(entry);
|
|
}
|
|
});
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
function filterMatches() {
|
|
if (filterType === 'unmatchedAkkoma') return allData.unmatchedAkkoma;
|
|
if (!filterType || !filterValue) return allData.matches;
|
|
|
|
switch (filterType) {
|
|
case 'alcea':
|
|
return allData.matches.filter(match => match.value.includes(filterValue));
|
|
case 'akkoma':
|
|
return allData.matches.filter(match => match.preview.includes(filterValue) || match.url === filterValue);
|
|
case 'type':
|
|
return allData.matches.filter(match => match.type === filterValue);
|
|
default:
|
|
return allData.matches;
|
|
}
|
|
}
|
|
|
|
function updateStats() {
|
|
const stats = document.getElementById('stats');
|
|
const filtered = filterMatches();
|
|
|
|
stats.innerHTML = `
|
|
<span class="stat-item" id="alcea-stat">
|
|
Alcea entries with #acws: <span class="stat-count">${allData.alceaEntries.length}</span>
|
|
</span> |
|
|
<span class="stat-item" id="akkoma-stat">
|
|
Akkoma entries: <span class="stat-count">${allData.akkomaEntries.length}</span>
|
|
</span> |
|
|
<span class="stat-item" id="matches-stat">
|
|
Matches found: <span class="stat-count">${allData.matches.length}</span>
|
|
</span> |
|
|
<span class="stat-item" id="unmatched-akkoma-stat">
|
|
Unmatched Akkoma entries: <span class="stat-count">${allData.unmatchedAkkoma.length}</span>
|
|
</span>
|
|
${filterType ? `<br><div style="margin-top:10px;font-weight:normal;">Active filter: ${filterType} = ${filterValue} | <a href="${window.location.pathname}">Clear filter</a></div>` : ''}
|
|
<br>
|
|
<button class="refresh-button" id="refresh-data">Refresh Data</button>
|
|
`;
|
|
|
|
document.getElementById('alcea-stat').onclick = () => location.href = '?filter=alcea&value=all';
|
|
document.getElementById('akkoma-stat').onclick = () => location.href = '?filter=akkoma&value=all';
|
|
document.getElementById('matches-stat').onclick = () => location.href = '?filter=matches&value=all';
|
|
document.getElementById('unmatched-akkoma-stat').onclick = () => location.href = '?filter=unmatchedAkkoma&value=1';
|
|
document.getElementById('refresh-data').onclick = loadData;
|
|
|
|
if (filterType) {
|
|
const el = document.getElementById(`${filterType}-stat`);
|
|
if (el) el.classList.add('filter-active');
|
|
}
|
|
}
|
|
|
|
function displayMatches() {
|
|
const matchesDiv = document.getElementById('matches');
|
|
const filtered = filterMatches();
|
|
|
|
if (filtered.length === 0) {
|
|
matchesDiv.innerHTML = '<div class="no-matches">No matches found with the current filter.</div>';
|
|
return;
|
|
}
|
|
|
|
//tlpreview
|
|
matchesDiv.innerHTML = '';
|
|
filtered.forEach(entry => {
|
|
const div = document.createElement('div');
|
|
div.className = 'match';
|
|
if (entry.type) {
|
|
// Build preview link URL using first 40 chars of value (with linebreaks replaced)
|
|
const rawValue = entry.value;
|
|
const sliced = rawValue
|
|
.slice(0, 40)
|
|
.replace(/\r\n/g, '\n')
|
|
.replace(/\r/g, '\n')
|
|
.replace(/\n/g, '\r\n');
|
|
|
|
let previewTextParam = '';
|
|
try {
|
|
previewTextParam = encodeURIComponent(sliced);
|
|
} catch (e) {
|
|
console.warn('Encoding failed for preview link text:', sliced, e);
|
|
previewTextParam = '';
|
|
}
|
|
const previewUrl = `https://alceawis.de/other/extra/scripts/fakesocialmedia/commentload.html?text=${previewTextParam}&number=8000`;
|
|
|
|
|
|
|
|
div.innerHTML = `
|
|
<div><strong>Match type:</strong> ${entry.type}
|
|
<span class="match-type ${entry.type}">${entry.type}</span></div>
|
|
<div class="url"><strong>Akkoma URL:</strong> <a href="${entry.url}" target="_blank">${entry.url}</a></div>
|
|
<div class="preview"><strong>Preview:</strong> ${entry.preview}</div>
|
|
<div><strong>Alcea value snippet:</strong>
|
|
<pre>${entry.value.slice(0, 500)}${entry.value.length > 500 ? '...' : ''}</pre>
|
|
<a class="preview-link" href="${previewUrl}" target="_blank">View in preview viewer ↗</a>
|
|
</div>
|
|
`;
|
|
} else {
|
|
div.innerHTML = `
|
|
<div class="url"><strong>Akkoma URL:</strong> <a href="${entry.url}" target="_blank">${entry.url}</a></div>
|
|
<div class="preview"><strong>Preview:</strong> ${entry.preview}</div>
|
|
<div><em>No matching Alcea entry found.</em></div>
|
|
`;
|
|
}
|
|
|
|
matchesDiv.appendChild(div);
|
|
});
|
|
}
|
|
|
|
async function loadData() {
|
|
const stats = document.getElementById('stats');
|
|
const matchesDiv = document.getElementById('matches');
|
|
|
|
stats.textContent = 'Loading data...';
|
|
matchesDiv.innerHTML = '<div class="loading">Loading data...</div>';
|
|
|
|
try {
|
|
const [alceaJson, akkomaJson] = await Promise.all([
|
|
fetchJson('/other/extra/scripts/fakesocialmedia/data_alcea.json'),
|
|
fetchJson('/other/extra/scripts/fakesocialmedia/0ld/data_akkoma.json')
|
|
]);
|
|
|
|
allData.alceaEntries = extractAlceaEntries(alceaJson);
|
|
allData.akkomaEntries = Array.isArray(akkomaJson) ? akkomaJson : Object.values(akkomaJson);
|
|
allData.matches = [];
|
|
|
|
const matchedAkkomaUrls = new Set();
|
|
|
|
for (const alcea of allData.alceaEntries) {
|
|
const value = alcea.value;
|
|
const rawValue = value;
|
|
const normValue = normalizeText(value);
|
|
const noBreaksValue = removeLineBreaks(value);
|
|
const normValueCompletely = normalizeTextCompletely(value);
|
|
let matched = false;
|
|
|
|
for (const akkoma of allData.akkomaEntries) {
|
|
if (!akkoma.preview) continue;
|
|
const rawPreview = akkoma.preview;
|
|
const normPreview = normalizeText(rawPreview);
|
|
const noBreaksPreview = removeLineBreaks(rawPreview);
|
|
const normPreviewCompletely = normalizeTextCompletely(rawPreview);
|
|
const url = akkoma.url;
|
|
|
|
if (rawValue.startsWith(rawPreview)) {
|
|
allData.matches.push({ type: 'raw', preview: rawPreview, url, value });
|
|
matchedAkkomaUrls.add(url);
|
|
matched = true;
|
|
break;
|
|
}
|
|
if (normValue.startsWith(normPreview)) {
|
|
allData.matches.push({ type: 'normalized', preview: rawPreview, url, value });
|
|
matchedAkkomaUrls.add(url);
|
|
matched = true;
|
|
break;
|
|
}
|
|
if (noBreaksValue.startsWith(noBreaksPreview)) {
|
|
allData.matches.push({ type: 'noBreaks', preview: rawPreview, url, value });
|
|
matchedAkkomaUrls.add(url);
|
|
matched = true;
|
|
break;
|
|
}
|
|
if (normValue.indexOf(normPreview) >= 0 && normValue.indexOf(normPreview) <= 30) {
|
|
allData.matches.push({ type: 'loose', preview: rawPreview, url, value });
|
|
matchedAkkomaUrls.add(url);
|
|
matched = true;
|
|
break;
|
|
}
|
|
if (normValueCompletely.indexOf(normPreviewCompletely) >= 0 && normValueCompletely.indexOf(normPreviewCompletely) <= 30) {
|
|
allData.matches.push({ type: 'finalNormalized', preview: rawPreview, url, value });
|
|
matchedAkkomaUrls.add(url);
|
|
matched = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
allData.unmatchedAkkoma = allData.akkomaEntries.filter(ak => !matchedAkkomaUrls.has(ak.url));
|
|
updateStats();
|
|
displayMatches();
|
|
|
|
} catch (err) {
|
|
stats.textContent = '❌ Error loading or processing data';
|
|
stats.className = 'error';
|
|
matchesDiv.innerHTML = `
|
|
<div class="error">
|
|
<p><strong>Error Details:</strong></p>
|
|
<p>${err.message}</p>
|
|
<p>Check the browser console for more info.</p>
|
|
</div>
|
|
`;
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
loadData();
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|