diff --git a/config/blacklist-db.js b/config/blacklist-db.js
index 294c2c3..32146da 100644
--- a/config/blacklist-db.js
+++ b/config/blacklist-db.js
@@ -44,7 +44,14 @@ async function loadBlacklistFromDB() {
offensive: [],
titles: [],
brands: [],
- inappropriate: []
+ inappropriate: [],
+ racial: [],
+ religious: [],
+ disability: [],
+ leetspeak: [],
+ cyberbullying: [],
+ drugs: [],
+ violence: []
};
// Erstelle neuen Trigram-Index
@@ -101,7 +108,14 @@ function getStaticBlacklist() {
'sex', 'porn', 'porno', 'fuck', 'shit',
'bitch', 'whore', 'prostitute',
'drug', 'cocaine', 'heroin', 'marijuana'
- ]
+ ],
+ racial: [],
+ religious: [],
+ disability: [],
+ leetspeak: [],
+ cyberbullying: [],
+ drugs: [],
+ violence: []
};
}
@@ -219,7 +233,14 @@ function getCategoryReason(category) {
offensive: 'Beleidigender oder anstößiger Begriff',
titles: 'Titel oder Berufsbezeichnung',
brands: 'Markenname',
- inappropriate: 'Unpassender Begriff'
+ inappropriate: 'Unpassender Begriff',
+ racial: 'Rassistischer oder ethnisch beleidigender Begriff',
+ religious: 'Religiös beleidigender oder blasphemischer Begriff',
+ disability: 'Beleidigender Begriff bezüglich Behinderungen',
+ leetspeak: 'Verschleierter beleidigender Begriff',
+ cyberbullying: 'Cyberbullying oder Online-Belästigung',
+ drugs: 'Drogenbezogener Begriff',
+ violence: 'Gewalt- oder bedrohungsbezogener Begriff'
};
return reasons[category] || 'Unzulässiger Begriff';
diff --git a/config/llm-blacklist.js b/config/llm-blacklist.js
deleted file mode 100644
index 80e6d87..0000000
--- a/config/llm-blacklist.js
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * LLM-basierte Blacklist-Prüfung mit Ollama
- * Verwendet ein lokales LLM zur intelligenten Bewertung von Namen
- */
-
-const axios = require('axios');
-
-// Ollama-Konfiguration
-const OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';
-const OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3.2:3b'; // Schnelles, kleines Modell
-
-/**
- * Prüft einen Namen mit dem LLM
- * @param {string} firstname - Vorname
- * @param {string} lastname - Nachname
- * @returns {Object} - {isBlocked: boolean, reason: string, confidence: number}
- */
-async function checkNameWithLLM(firstname, lastname) {
- if (!firstname || !lastname) {
- return { isBlocked: false, reason: '', confidence: 0 };
- }
-
- try {
- const fullName = `${firstname} ${lastname}`;
-
- // Prompt für das LLM
- const prompt = `Du bist ein strenger Moderator für ein Spielsystem. Prüfe ob der Name "${fullName}" für die Verwendung geeignet ist.
-
-WICHTIG: Blockiere ALLE Namen die:
-- Historisch belastet sind (Adolf Hitler, Stalin, Mussolini, etc.)
-- Beleidigend oder anstößig sind (Satan, Idiot, etc.)
-- Unpassende Titel sind (Dr., Professor, etc.)
-- Markennamen sind (Coca-Cola, Nike, etc.)
-- Andere unangemessene Inhalte haben
-
-Antworte NUR mit "TRUE" (blockiert) oder "FALSE" (erlaubt) - keine Erklärungen.
-
-Name: "${fullName}"
-Antwort:`;
-
- // Ollama API-Aufruf
- const response = await axios.post(`${OLLAMA_BASE_URL}/api/generate`, {
- model: OLLAMA_MODEL,
- prompt: prompt,
- stream: false,
- options: {
- temperature: 0.1, // Niedrige Temperatur für konsistente Antworten
- top_p: 0.9,
- max_tokens: 10 // Nur TRUE/FALSE erwartet
- }
- }, {
- timeout: 10000 // 10 Sekunden Timeout
- });
-
- const llmResponse = response.data.response.trim().toUpperCase();
-
- // Parse LLM-Antwort
- let isBlocked = false;
- let reason = '';
- let confidence = 0.8; // Standard-Konfidenz für LLM
-
- if (llmResponse === 'TRUE') {
- isBlocked = true;
- reason = 'Name wurde vom KI-Moderator als ungeeignet eingestuft';
- } else if (llmResponse === 'FALSE') {
- isBlocked = false;
- reason = 'Name wurde vom KI-Moderator als geeignet eingestuft';
- } else {
- // Fallback bei unerwarteter Antwort
- console.warn(`Unerwartete LLM-Antwort: "${llmResponse}" für Name: "${fullName}"`);
- isBlocked = false;
- reason = 'KI-Moderator konnte Name nicht eindeutig bewerten';
- confidence = 0.3;
- }
-
- return {
- isBlocked,
- reason,
- confidence,
- llmResponse: llmResponse,
- matchType: 'llm'
- };
-
- } catch (error) {
- console.error('Error checking name with LLM:', error);
-
- // Fallback bei LLM-Fehlern
- return {
- isBlocked: false,
- reason: 'KI-Moderator nicht verfügbar - Name wurde erlaubt',
- confidence: 0.1,
- error: error.message,
- matchType: 'llm-error'
- };
- }
-}
-
-/**
- * Testet die LLM-Verbindung
- * @returns {Object} - {connected: boolean, model: string, error?: string}
- */
-async function testLLMConnection() {
- try {
- const response = await axios.post(`${OLLAMA_BASE_URL}/api/generate`, {
- model: OLLAMA_MODEL,
- prompt: 'Test',
- stream: false,
- options: {
- max_tokens: 1
- }
- }, {
- timeout: 5000
- });
-
- return {
- connected: true,
- model: OLLAMA_MODEL,
- baseUrl: OLLAMA_BASE_URL
- };
- } catch (error) {
- return {
- connected: false,
- model: OLLAMA_MODEL,
- baseUrl: OLLAMA_BASE_URL,
- error: error.message
- };
- }
-}
-
-/**
- * Erweiterte LLM-Prüfung mit Kontext
- * @param {string} firstname - Vorname
- * @param {string} lastname - Nachname
- * @param {string} context - Zusätzlicher Kontext (optional)
- * @returns {Object} - Prüfungsergebnis
- */
-async function checkNameWithContext(firstname, lastname, context = '') {
- if (!firstname || !lastname) {
- return { isBlocked: false, reason: '', confidence: 0 };
- }
-
- try {
- const fullName = `${firstname} ${lastname}`;
-
- // Erweiterter Prompt mit Kontext
- const prompt = `Du bist ein Moderator für ein Spielsystem. Prüfe ob der Name "${fullName}" für die Verwendung geeignet ist.
-
-Kontext: ${context || 'Standard-Spielname'}
-
-Beurteile den Namen basierend auf:
-- Historisch belastete Namen (z.B. Adolf Hitler, Stalin, etc.)
-- Beleidigende oder anstößige Begriffe
-- Unpassende Titel oder Berufsbezeichnungen
-- Markennamen die nicht verwendet werden sollten
-- Andere unangemessene Inhalte
-
-Antworte NUR mit "TRUE" oder "FALSE" - keine Erklärungen.
-
-Name: "${fullName}"
-Antwort:`;
-
- const response = await axios.post(`${OLLAMA_BASE_URL}/api/generate`, {
- model: OLLAMA_MODEL,
- prompt: prompt,
- stream: false,
- options: {
- temperature: 0.1,
- top_p: 0.9,
- max_tokens: 10
- }
- }, {
- timeout: 10000
- });
-
- const llmResponse = response.data.response.trim().toUpperCase();
-
- let isBlocked = false;
- let reason = '';
- let confidence = 0.8;
-
- if (llmResponse === 'TRUE') {
- isBlocked = true;
- reason = 'Name wurde vom KI-Moderator als ungeeignet eingestuft';
- } else if (llmResponse === 'FALSE') {
- isBlocked = false;
- reason = 'Name wurde vom KI-Moderator als geeignet eingestuft';
- } else {
- console.warn(`Unerwartete LLM-Antwort: "${llmResponse}" für Name: "${fullName}"`);
- isBlocked = false;
- reason = 'KI-Moderator konnte Name nicht eindeutig bewerten';
- confidence = 0.3;
- }
-
- return {
- isBlocked,
- reason,
- confidence,
- llmResponse: llmResponse,
- matchType: 'llm-context',
- context: context
- };
-
- } catch (error) {
- console.error('Error checking name with LLM context:', error);
-
- return {
- isBlocked: false,
- reason: 'KI-Moderator nicht verfügbar - Name wurde erlaubt',
- confidence: 0.1,
- error: error.message,
- matchType: 'llm-error'
- };
- }
-}
-
-/**
- * Batch-Prüfung mehrerer Namen
- * @param {Array} names - Array von {firstname, lastname} Objekten
- * @returns {Array} - Array von Prüfungsergebnissen
- */
-async function checkNamesBatch(names) {
- const results = [];
-
- for (const name of names) {
- try {
- const result = await checkNameWithLLM(name.firstname, name.lastname);
- results.push({
- ...name,
- ...result
- });
- } catch (error) {
- results.push({
- ...name,
- isBlocked: false,
- reason: 'Fehler bei der Prüfung',
- confidence: 0,
- error: error.message,
- matchType: 'error'
- });
- }
- }
-
- return results;
-}
-
-module.exports = {
- checkNameWithLLM,
- checkNameWithContext,
- checkNamesBatch,
- testLLMConnection,
- OLLAMA_BASE_URL,
- OLLAMA_MODEL
-};
diff --git a/public/admin-dashboard.html b/public/admin-dashboard.html
index d17bf0b..1262d1c 100644
--- a/public/admin-dashboard.html
+++ b/public/admin-dashboard.html
@@ -106,12 +106,6 @@
-
-
-
🧠 KI-Moderator
-
Intelligente Namensprüfung mit Ollama LLM
-
-
@@ -211,6 +205,13 @@
+
+
+
+
+
+
+
@@ -237,49 +238,6 @@
-
-
-
-
×
-
🧠 KI-Moderator
-
-
-
-
-
-
-
Name testen
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Vergleich mit Blacklist
-
-
-
-
diff --git a/public/js/admin-dashboard.js b/public/js/admin-dashboard.js
index 0357a12..6c2b510 100644
--- a/public/js/admin-dashboard.js
+++ b/public/js/admin-dashboard.js
@@ -893,7 +893,14 @@ function getCategoryIcon(category) {
offensive: '⚠️',
titles: '👑',
brands: '🏷️',
- inappropriate: '🚫'
+ inappropriate: '🚫',
+ racial: '🌍',
+ religious: '⛪',
+ disability: '♿',
+ leetspeak: '🔤',
+ cyberbullying: '💻',
+ drugs: '💊',
+ violence: '⚔️'
};
return icons[category] || '📝';
}
@@ -904,7 +911,14 @@ function getCategoryDisplayName(category) {
offensive: 'Beleidigend/anstößig',
titles: 'Titel/Berufsbezeichnung',
brands: 'Markenname',
- inappropriate: 'Unpassend'
+ inappropriate: 'Unpassend',
+ racial: 'Rassistisch/ethnisch',
+ religious: 'Religiös beleidigend',
+ disability: 'Behinderungsbezogen',
+ leetspeak: 'Verschleiert',
+ cyberbullying: 'Cyberbullying',
+ drugs: 'Drogenbezogen',
+ violence: 'Gewalt/Bedrohung'
};
return names[category] || category;
}
@@ -1103,196 +1117,7 @@ function displayBlacklistStats(stats) {
statsDiv.innerHTML = html;
}
-// LLM-Management
-function showLLMManagement() {
- document.getElementById('llmModal').style.display = 'block';
- loadLLMStatus();
-}
-function closeLLMModal() {
- document.getElementById('llmModal').style.display = 'none';
-}
-// LLM-Status laden
-async function loadLLMStatus() {
- try {
- const response = await fetch('/api/v1/admin/llm/status', {
- headers: {
- 'Authorization': `Bearer ${localStorage.getItem('adminToken')}`
- }
- });
- const result = await response.json();
- const statusContent = document.getElementById('llmStatusContent');
- if (result.success) {
- const status = result.data;
- if (status.connected) {
- statusContent.innerHTML = `
-
- ✅ KI-Moderator verbunden
- Modell: ${status.model}
- URL: ${status.baseUrl}
-
- `;
- } else {
- statusContent.innerHTML = `
-
- ❌ KI-Moderator nicht verfügbar
- Modell: ${status.model}
- URL: ${status.baseUrl}
- Fehler: ${status.error}
-
- `;
- }
- } else {
- statusContent.innerHTML = `
-
- ❌ Fehler beim Laden des Status
- ${result.message}
-
- `;
- }
- } catch (error) {
- console.error('Error loading LLM status:', error);
- document.getElementById('llmStatusContent').innerHTML = `
-
- ❌ Fehler beim Laden des Status
- ${error.message}
-
- `;
- }
-}
-
-// Name mit LLM testen
-async function testNameWithLLM() {
- const firstname = document.getElementById('llmFirstname').value.trim();
- const lastname = document.getElementById('llmLastname').value.trim();
- const context = document.getElementById('llmContext').value.trim();
-
- if (!firstname || !lastname) {
- showLLMMessage('Bitte gib Vorname und Nachname ein', 'error');
- return;
- }
-
- try {
- // LLM-Test
- const llmResponse = await fetch('/api/v1/admin/llm/test', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${localStorage.getItem('adminToken')}`
- },
- body: JSON.stringify({ firstname, lastname, context })
- });
-
- const llmResult = await llmResponse.json();
-
- // Blacklist-Test zum Vergleich
- const blacklistResponse = await fetch('/api/v1/admin/blacklist/test', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${localStorage.getItem('adminToken')}`
- },
- body: JSON.stringify({ firstname, lastname })
- });
-
- const blacklistResult = await blacklistResponse.json();
-
- // Ergebnisse anzeigen
- displayLLMResults(llmResult, blacklistResult);
-
- } catch (error) {
- console.error('Error testing name with LLM:', error);
- showLLMMessage('Fehler beim Testen: ' + error.message, 'error');
- }
-}
-
-// LLM-Ergebnisse anzeigen
-function displayLLMResults(llmResult, blacklistResult) {
- const resultDiv = document.getElementById('llmResult');
- const resultContent = document.getElementById('llmResultContent');
- const comparisonDiv = document.getElementById('llmComparison');
- const comparisonContent = document.getElementById('llmComparisonContent');
-
- if (llmResult.success) {
- const llm = llmResult.data;
-
- let llmStatus = '';
- if (llm.isBlocked) {
- llmStatus = `
-
- ❌ Name blockiert
- Grund: ${llm.reason}
- Konfidenz: ${Math.round(llm.confidence * 100)}%
- LLM-Antwort: "${llm.llmResponse}"
- Typ: ${llm.matchType}
-
- `;
- } else {
- llmStatus = `
-
- ✅ Name erlaubt
- Grund: ${llm.reason}
- Konfidenz: ${Math.round(llm.confidence * 100)}%
- LLM-Antwort: "${llm.llmResponse}"
- Typ: ${llm.matchType}
-
- `;
- }
-
- resultContent.innerHTML = llmStatus;
- resultDiv.style.display = 'block';
-
- // Vergleich mit Blacklist
- if (blacklistResult.success) {
- const blacklist = blacklistResult.data;
- let comparisonStatus = '';
-
- if (blacklist.combined.isBlocked) {
- comparisonStatus = `
-
- ⚠️ Name blockiert (${blacklist.combined.source})
- Grund: ${blacklist.combined.reason}
-
- `;
- } else {
- comparisonStatus = `
-
- ✅ Name erlaubt
- Sowohl KI als auch Blacklist erlauben den Namen
-
- `;
- }
-
- comparisonContent.innerHTML = comparisonStatus;
- comparisonDiv.style.display = 'block';
- }
-
- } else {
- resultContent.innerHTML = `
-
- ❌ Fehler beim Testen
- ${llmResult.message}
-
- `;
- resultDiv.style.display = 'block';
- }
-}
-
-// LLM-Nachricht anzeigen
-function showLLMMessage(message, type) {
- const resultDiv = document.getElementById('llmResult');
- const resultContent = document.getElementById('llmResultContent');
-
- const color = type === 'error' ? '#c62828' : '#2e7d32';
- const bgColor = type === 'error' ? '#ffebee' : '#e8f5e8';
-
- resultContent.innerHTML = `
-
- ${type === 'error' ? '❌' : '✅'} ${message}
-
- `;
- resultDiv.style.display = 'block';
-}
diff --git a/routes/api.js b/routes/api.js
index 1085e63..4491779 100644
--- a/routes/api.js
+++ b/routes/api.js
@@ -1019,7 +1019,6 @@ router.post('/v1/private/users/find', requireApiKey, async (req, res) => {
// Import blacklist module
const { checkNameAgainstBlacklist, addToBlacklist, removeFromBlacklist, getBlacklist } = require('../config/blacklist-db');
-const { checkNameWithLLM, checkNameWithContext, testLLMConnection } = require('../config/llm-blacklist');
// Create new player with RFID and blacklist validation (no auth required for dashboard)
router.post('/v1/public/players/create-with-rfid', async (req, res) => {
@@ -1034,27 +1033,13 @@ router.post('/v1/public/players/create-with-rfid', async (req, res) => {
}
try {
- // LLM-basierte Blacklist-Prüfung
- const llmCheck = await checkNameWithLLM(firstname, lastname);
- if (llmCheck.isBlocked) {
- return res.status(400).json({
- success: false,
- message: `Name nicht zulässig: ${llmCheck.reason}`,
- details: llmCheck
- });
- }
-
- // Fallback: Traditionelle Blacklist-Prüfung (optional)
+ // Blacklist-Prüfung mit Levenshtein-Algorithmus
const blacklistCheck = await checkNameAgainstBlacklist(firstname, lastname);
if (blacklistCheck.isBlocked) {
return res.status(400).json({
success: false,
message: `Name nicht zulässig: ${blacklistCheck.reason}`,
- details: {
- reason: blacklistCheck.reason,
- category: blacklistCheck.category,
- matchedTerm: blacklistCheck.matchedTerm
- }
+ details: blacklistCheck
});
}
@@ -1618,22 +1603,16 @@ router.post('/v1/admin/blacklist/test', requireAdminAuth, async (req, res) => {
}
try {
- // LLM-Prüfung
- const llmResult = await checkNameWithLLM(firstname, lastname);
-
- // Traditionelle Blacklist-Prüfung
+ // Blacklist-Prüfung mit Levenshtein-Algorithmus
const blacklistResult = await checkNameAgainstBlacklist(firstname, lastname);
res.json({
success: true,
data: {
- llm: llmResult,
blacklist: blacklistResult,
- combined: {
- isBlocked: llmResult.isBlocked || blacklistResult.isBlocked,
- reason: llmResult.isBlocked ? llmResult.reason : blacklistResult.reason,
- source: llmResult.isBlocked ? 'llm' : (blacklistResult.isBlocked ? 'blacklist' : 'none')
- }
+ isBlocked: blacklistResult.isBlocked,
+ reason: blacklistResult.reason,
+ source: blacklistResult.isBlocked ? 'blacklist' : 'none'
}
});
} catch (error) {