minor changes

This commit is contained in:
2025-11-24 16:32:13 +01:00
parent 3c23aae864
commit 6d0cada610
14 changed files with 1059 additions and 491 deletions

View File

@@ -480,6 +480,127 @@
</form>
</section>
<!-- Sports scoring Section -->
<section class="settings-section">
<h2 class="section-title">🏷️ Sport-Post-Scoring</h2>
<p class="section-description">
Analysiert Beitragstexte nach Sport-Begriffen (z.B. Fußball, Volleyball) und weist einen Score zu.
Beiträge oberhalb des Schwellwerts würden später automatisch ausgeblendet aktuell wird nur markiert.
</p>
<form id="moderationSettingsForm">
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="sportsScoringEnabled" class="form-checkbox">
<span>Scoring aktivieren</span>
</label>
<p class="form-help">
Nutze das heuristische Punktesystem, um offensichtliche Sport-/Spiel-Posts zu erkennen.
</p>
</div>
<div class="form-group">
<label for="sportsScoreThreshold" class="form-label">Schwellwert für Sport-Posts</label>
<input type="number" id="sportsScoreThreshold" class="form-input" min="0" max="50" step="0.5" value="5">
<p class="form-help">
Ab diesem Score kann der Post automatisch versteckt werden.
</p>
</div>
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="sportsAutoHideEnabled" class="form-checkbox">
<span>Automatisch verstecken bei Überschreitung</span>
</label>
<p class="form-help">
Wenn aktiviert, blendet die Extension Posts mit Score ≥ Schwellwert automatisch aus (Feed & Suche).
</p>
</div>
<div class="form-group">
<label class="form-label">Gewichte (010)</label>
<div class="grid-weights">
<label class="form-field-inline">
<span>Ergebnis (1:0)</span>
<input type="number" id="sportWeightScoreline" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Score-Emojis (+3⃣)</span>
<input type="number" id="sportWeightScoreEmoji" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Sport-Emojis (⚽️)</span>
<input type="number" id="sportWeightSportEmoji" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Verben (gewinnen)</span>
<input type="number" id="sportWeightSportVerb" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Nomen (Liga, Tor)</span>
<input type="number" id="sportWeightSportNoun" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Hashtags (#auswärtssieg)</span>
<input type="number" id="sportWeightHashtag" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Team-Kürzel (FC…)</span>
<input type="number" id="sportWeightTeamToken" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Wettbewerbe (Cup)</span>
<input type="number" id="sportWeightCompetition" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Ergebnisbezug (Sieg)</span>
<input type="number" id="sportWeightCelebration" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Ort (Auswärts)</span>
<input type="number" id="sportWeightLocation" class="form-input" min="0" max="10" step="0.5">
</label>
</div>
<p class="form-help">
Je höher das Gewicht, desto stärker zahlt der jeweilige Treffer auf den Sport-Score ein.
</p>
</div>
<div class="form-group">
<label class="form-label">Stichwort-Listen (kommagetrennt)</label>
<div class="grid-weights">
<label class="form-field-inline">
<span>Nomen</span>
<textarea id="sportTermsNouns" class="form-textarea" rows="2" placeholder="auswärtssieg, liga, tor ..."></textarea>
</label>
<label class="form-field-inline">
<span>Verben</span>
<textarea id="sportTermsVerbs" class="form-textarea" rows="2" placeholder="gewinnen, punkten ..."></textarea>
</label>
<label class="form-field-inline">
<span>Wettbewerbe</span>
<textarea id="sportTermsCompetitions" class="form-textarea" rows="2" placeholder="bundesliga, cup ..."></textarea>
</label>
<label class="form-field-inline">
<span>Ergebnisbezug</span>
<textarea id="sportTermsCelebrations" class="form-textarea" rows="2" placeholder="sieg, tabellenführung ..."></textarea>
</label>
<label class="form-field-inline">
<span>Orte</span>
<textarea id="sportTermsLocations" class="form-textarea" rows="2" placeholder="auswärts, stadion ..."></textarea>
</label>
<label class="form-field-inline">
<span>Negativliste</span>
<textarea id="sportTermsNegatives" class="form-textarea" rows="2" placeholder="rezept, politik ..."></textarea>
</label>
</div>
<p class="form-help">
Leere Felder nutzen die Standardliste. Negativliste senkt den Score bei Treffern.
</p>
</div>
</form>
</section>
<!-- Hidden posts / purge settings -->
<section class="settings-section">
<h2 class="section-title">Versteckte Beiträge bereinigen</h2>

View File

@@ -95,6 +95,20 @@
line-height: 1.4;
}
.grid-weights {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px;
}
.form-field-inline {
display: flex;
flex-direction: column;
gap: 6px;
font-size: 13px;
color: #1c1e21;
}
.form-help a {
color: #1877f2;
text-decoration: none;

View File

@@ -44,6 +44,33 @@ const PROVIDER_INFO = {
let credentials = [];
let currentSettings = null;
let hiddenSettings = { auto_purge_enabled: true, retention_days: 90 };
const SPORT_WEIGHT_FIELDS = [
{ key: 'scoreline', id: 'sportWeightScoreline' },
{ key: 'scoreEmoji', id: 'sportWeightScoreEmoji' },
{ key: 'sportEmoji', id: 'sportWeightSportEmoji' },
{ key: 'sportVerb', id: 'sportWeightSportVerb' },
{ key: 'sportNoun', id: 'sportWeightSportNoun' },
{ key: 'hashtag', id: 'sportWeightHashtag' },
{ key: 'teamToken', id: 'sportWeightTeamToken' },
{ key: 'competition', id: 'sportWeightCompetition' },
{ key: 'celebration', id: 'sportWeightCelebration' },
{ key: 'location', id: 'sportWeightLocation' }
];
const SPORT_TERM_FIELDS = [
{ key: 'nouns', id: 'sportTermsNouns', placeholder: 'auswärtssieg, liga, tor ...' },
{ key: 'verbs', id: 'sportTermsVerbs', placeholder: 'gewinnen, punkten ...' },
{ key: 'competitions', id: 'sportTermsCompetitions', placeholder: 'bundesliga, cup ...' },
{ key: 'celebrations', id: 'sportTermsCelebrations', placeholder: 'sieg, tabellenführung ...' },
{ key: 'locations', id: 'sportTermsLocations', placeholder: 'auswärts, stadion ...' },
{ key: 'negatives', id: 'sportTermsNegatives', placeholder: 'rezept, politik ...' }
];
let moderationSettings = {
sports_scoring_enabled: true,
sports_score_threshold: 5,
sports_score_weights: {},
sports_terms: {},
sports_auto_hide_enabled: false
};
function apiFetch(url, options = {}) {
return fetch(url, {...options, credentials: 'include'});
@@ -213,6 +240,153 @@ async function purgeHiddenNow() {
}
}
function normalizeSportsScoreThresholdInput(value) {
const parsed = parseFloat(value);
if (Number.isNaN(parsed) || parsed < 0) {
return 5;
}
return Math.min(50, Math.max(0, parsed));
}
function normalizeSportWeightInput(value, fallback = 1) {
const parsed = parseFloat(value);
if (Number.isNaN(parsed) || parsed < 0) {
return fallback;
}
return Math.min(10, Math.max(0, parsed));
}
function applyWeightInputs(enabled) {
SPORT_WEIGHT_FIELDS.forEach(({ id }) => {
const input = document.getElementById(id);
if (input) {
input.disabled = !enabled;
}
});
}
function applyTermInputs(enabled) {
SPORT_TERM_FIELDS.forEach(({ id }) => {
const input = document.getElementById(id);
if (input) {
input.disabled = !enabled;
}
});
}
function serializeTermListInput(value) {
if (!value || typeof value !== 'string') return [];
return value
.split(',')
.map((entry) => entry.trim())
.filter((entry) => entry)
.slice(0, 200);
}
function renderTermList(list) {
if (!Array.isArray(list) || !list.length) return '';
return list.join(', ');
}
function applyModerationSettingsUI() {
const enabledToggle = document.getElementById('sportsScoringEnabled');
const thresholdInput = document.getElementById('sportsScoreThreshold');
const autoHideToggle = document.getElementById('sportsAutoHideEnabled');
if (enabledToggle) {
enabledToggle.checked = !!moderationSettings.sports_scoring_enabled;
}
if (thresholdInput) {
thresholdInput.value = moderationSettings.sports_score_threshold ?? 5;
thresholdInput.disabled = !enabledToggle?.checked;
}
if (autoHideToggle) {
autoHideToggle.checked = !!moderationSettings.sports_auto_hide_enabled;
autoHideToggle.disabled = !enabledToggle?.checked;
}
SPORT_WEIGHT_FIELDS.forEach(({ key, id }) => {
const input = document.getElementById(id);
if (input) {
const value = moderationSettings.sports_score_weights?.[key];
input.value = typeof value === 'number' ? value : '';
input.disabled = !enabledToggle?.checked;
}
});
SPORT_TERM_FIELDS.forEach(({ key, id }) => {
const input = document.getElementById(id);
if (input) {
input.value = renderTermList(moderationSettings.sports_terms?.[key]);
input.disabled = !enabledToggle?.checked;
}
});
}
async function loadModerationSettings() {
const res = await apiFetch(`${API_URL}/moderation-settings`);
if (!res.ok) throw new Error('Konnte Moderations-Einstellungen nicht laden');
const data = await res.json();
moderationSettings = {
sports_scoring_enabled: !!data.sports_scoring_enabled,
sports_score_threshold: normalizeSportsScoreThresholdInput(data.sports_score_threshold),
sports_score_weights: data.sports_score_weights || {},
sports_terms: data.sports_terms || {},
sports_auto_hide_enabled: !!data.sports_auto_hide_enabled
};
applyModerationSettingsUI();
}
async function saveModerationSettings(event, { silent = false } = {}) {
if (event && typeof event.preventDefault === 'function') {
event.preventDefault();
}
const enabledToggle = document.getElementById('sportsScoringEnabled');
const thresholdInput = document.getElementById('sportsScoreThreshold');
const autoHideToggle = document.getElementById('sportsAutoHideEnabled');
const enabled = enabledToggle ? enabledToggle.checked : true;
const threshold = thresholdInput
? normalizeSportsScoreThresholdInput(thresholdInput.value)
: moderationSettings.sports_score_threshold;
const autoHide = autoHideToggle ? autoHideToggle.checked : false;
const weights = {};
SPORT_WEIGHT_FIELDS.forEach(({ key, id }) => {
const input = document.getElementById(id);
weights[key] = normalizeSportWeightInput(input ? input.value : moderationSettings.sports_score_weights?.[key], moderationSettings.sports_score_weights?.[key] ?? 1);
});
const terms = {};
SPORT_TERM_FIELDS.forEach(({ key, id }) => {
const input = document.getElementById(id);
terms[key] = serializeTermListInput(input ? input.value : moderationSettings.sports_terms?.[key]);
});
try {
const res = await apiFetch(`${API_URL}/moderation-settings`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sports_scoring_enabled: enabled,
sports_score_threshold: threshold,
sports_auto_hide_enabled: autoHide,
sports_score_weights: weights,
sports_terms: terms
})
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.error || 'Fehler beim Speichern');
}
moderationSettings = await res.json();
applyModerationSettingsUI();
if (!silent) {
showSuccess('✅ Sport-Scoring gespeichert');
}
return true;
} catch (err) {
if (!silent) {
showError('❌ ' + err.message);
}
return false;
}
}
function shorten(text, maxLength = 80) {
if (typeof text !== 'string') {
return '';
@@ -848,6 +1022,7 @@ async function saveAllSettings(event) {
const results = await Promise.all([
saveSettings(null, { silent: true }),
saveHiddenSettings(null, { silent: true }),
saveModerationSettings(null, { silent: true }),
saveAllFriends({ silent: true })
]);
@@ -978,6 +1153,49 @@ if (autoPurgeHiddenToggle) {
});
}
const sportsScoringToggle = document.getElementById('sportsScoringEnabled');
const sportsScoreInput = document.getElementById('sportsScoreThreshold');
if (sportsScoringToggle && sportsScoreInput) {
sportsScoringToggle.addEventListener('change', () => {
sportsScoreInput.disabled = !sportsScoringToggle.checked;
applyWeightInputs(sportsScoringToggle.checked);
applyTermInputs(sportsScoringToggle.checked);
const autoHideToggle = document.getElementById('sportsAutoHideEnabled');
if (autoHideToggle) {
autoHideToggle.disabled = !sportsScoringToggle.checked;
}
});
sportsScoreInput.addEventListener('blur', () => {
sportsScoreInput.value = normalizeSportsScoreThresholdInput(sportsScoreInput.value);
});
SPORT_WEIGHT_FIELDS.forEach(({ id }) => {
const input = document.getElementById(id);
if (input) {
input.addEventListener('blur', () => {
input.value = normalizeSportWeightInput(input.value);
});
}
});
SPORT_TERM_FIELDS.forEach(({ id }) => {
const input = document.getElementById(id);
if (input) {
input.addEventListener('blur', () => {
input.value = renderTermList(serializeTermListInput(input.value));
});
}
});
const moderationForm = document.getElementById('moderationSettingsForm');
if (moderationForm) {
moderationForm.addEventListener('submit', (e) => saveModerationSettings(e));
}
}
// Initialize
Promise.all([loadCredentials(), loadSettings(), loadHiddenSettings(), loadProfileFriends()]).catch(err => showError(err.message));
Promise.all([
loadCredentials(),
loadSettings(),
loadHiddenSettings(),
loadModerationSettings(),
loadProfileFriends()
]).catch(err => showError(err.message));
})();