Files
BSN-Chatsystem/verify_article.sh
T

210 lines
9.0 KiB
Bash
Executable File

#!/bin/bash
# verify_article.sh — Prüft Artikel-HTML auf Einhaltung aller redaktionellen Standards
# Usage: ./verify_article.sh <artikel_datei.html>
# Exit 0 = alle Checks bestanden, Exit 1 = Fehler gefunden
#
# Aufruf aus Cronjobs: bash /home/hermes/workspace/verify_article.sh /home/hermes/workspace/artikel_*.html
set -euo pipefail
FILE="${1:-}"
ERRORS=0
WARNINGS=0
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
ok() { echo -e "${GREEN}$1${NC}"; }
fail() { echo -e "${RED}$1${NC}"; ERRORS=$((ERRORS+1)); }
warn() { echo -e "${YELLOW}⚠️ $1${NC}"; WARNINGS=$((WARNINGS+1)); }
if [ -z "$FILE" ] || [ ! -f "$FILE" ]; then
echo "Usage: $0 <artikel_datei.html>"
exit 1
fi
echo "=== Verifiziere: $FILE ==="
echo ""
# ── CHECK 1: Exactly one read-more ──────────────────────────────────
# Nur echte HTML-Tags zählen, nicht &lt;hr...&gt; Referenzen in QC-Zeilen
READMORE_COUNT=$(grep -c '<hr id="system-readmore"' "$FILE" || true)
if [ "$READMORE_COUNT" = "1" ]; then
ok "Read-More: genau 1"
elif [ "$READMORE_COUNT" = "0" ]; then
fail "Read-More: KEIN <hr id=\"system-readmore\" /> gefunden"
else
fail "Read-More: $READMORE_COUNT Stück (muss genau 1 sein)"
fi
# ── CHECK 2: No <br> immediately after read-more ────────────────────
if grep -A1 'system-readmore' "$FILE" | grep -q '<br'; then
fail "<br> direkt nach Read-More (erzeugt doppelte Linie)"
else
ok "Kein <br> nach Read-More"
fi
# ── CHECK 3: p.meta not div.meta ────────────────────────────────────
if grep -q '<div class="meta">' "$FILE"; then
fail "<div class=\"meta\"> statt <p class=\"meta\"> (article_server regex schlägt fehl)"
else
ok "<p class=\"meta\"> korrekt"
fi
# ── CHECK 4: No emojis in article body (QC line excluded) ──────────
# Extract body between <article> and </article>, strip everything from QC line onward
BODY=$(sed -n '/<article>/,/<\/article>/p' "$FILE" | sed '/<p class="qc">/,$d')
if echo "$BODY" | python3 -c "import sys,re; t=sys.stdin.read(); sys.exit(0 if re.search(r'[\U0001F300-\U0001F9FF\U00002600-\U000027BF\U0001F600-\U0001F64F]',t) else 1)"; then
fail "Emojis im Artikel-Body gefunden"
else
ok "Keine Emojis"
fi
# ── CHECK 5: File ends with </html> ─────────────────────────────────
LAST_LINE=$(tail -1 "$FILE")
if echo "$LAST_LINE" | grep -q '</html>'; then
ok "Datei endet mit </html>"
else
fail "Letzte Zeile ist nicht </html> (Fremd-Content nach Artikel-Ende?)"
fi
# ── CHECK 6: No meta-line in body (title, date, reading time) ───────
if echo "$BODY" | grep -qP '<p[^>]*>.*\d{1,2}\.\s+\w+\s+\d{4}.*·.*Lesezeit'; then
fail "Meta-Zeile im Body (Datum · Lesezeit) — macht Article-Server selbst!"
fi
# ── CHECK 7: Sources heading uses span-strong, not h2 ───────────────
if echo "$BODY" | grep -q '<h2>Quellen</h2>'; then
fail "<h2>Quellen</h2> statt <span><strong>Quellen</strong></span>"
else
ok "Quellen-Überschrift korrekt (kein <h2>)"
fi
# ── CHECK 8: All links have target="_blank" ─────────────────────────
LINKS_TOTAL=$(echo "$BODY" | grep -o '<a ' | wc -l)
LINKS_BLANK=$(echo "$BODY" | grep -o 'target="_blank"' | wc -l)
if [ "$LINKS_TOTAL" -gt 0 ] && [ "$LINKS_TOTAL" != "$LINKS_BLANK" ]; then
MISSING=$((LINKS_TOTAL - LINKS_BLANK))
fail "$MISSING von $LINKS_TOTAL Links ohne target=\"_blank\""
else
ok "Alle $LINKS_TOTAL Links mit target=\"_blank\""
fi
# ── CHECK 9: Meta description present ───────────────────────────────
if grep -q '<meta name="description" content="[^"]*[a-zA-Z]' "$FILE"; then
ok "Meta-Description vorhanden"
else
fail "Meta-Description fehlt oder leer"
fi
# ── CHECK 10: No QC line outside <p class="qc"> ────────────────────
QC_COUNT=$(echo "$BODY" | grep -c 'class="qc"' || true)
QC_RAW=$(echo "$BODY" | grep -cP 'QC:\s*[✓✔]' || true)
if [ "$QC_RAW" -gt "$QC_COUNT" ]; then
fail "QC-Hinweis nicht in <p class=\"qc\"> (wird nicht gestrippt)"
else
ok "QC-Zeile korrekt in <p class=\"qc\">"
fi
# ── CHECK 11: Autoren gegen BGG verifizieren ─────────────────────────
echo ""
echo "--- BGG Autoren-Faktencheck ---"
AUTHOR_CHECK=$(python3 /home/hermes/workspace/check_authors.py "$FILE" 2>&1)
AUTHOR_EXIT=$?
echo "$AUTHOR_CHECK"
if [ $AUTHOR_EXIT -eq 1 ]; then
ERRORS=$((ERRORS + 1))
elif [ $AUTHOR_EXIT -eq 2 ]; then
WARNINGS=$((WARNINGS + 1))
fi
# ── CHECK 12: Keine XML-API-Referenzen im Artikel ──────────────────
if echo "$BODY" | grep -qiP 'xml\s*api|XMLAPI|xmlapi|betriebsgeheimnis'; then
fail "XML-API-Referenz oder 'Betriebsgeheimnis' im Artikel (nicht leser-tauglich)"
else
ok "Keine XML-API-Referenzen"
fi
# ── CHECK 13: Quellen-Block muss klickbare Links enthalten ──────────
# Sucht Quellen-Überschrift (h2, span, strong) und prüft ob danach Links kommen
SOURCES_SECTION=$(echo "$BODY" | sed -n '/<h2>Quellen<\/h2>/,$p; /<span.*>.*Quellen.*<\/span>/,$p' || echo "")
if [ -n "$SOURCES_SECTION" ]; then
SOURCE_LINKS=$(echo "$SOURCES_SECTION" | grep -c '<a href=' || true)
if [ "$SOURCE_LINKS" -eq 0 ]; then
fail "Quellen-Block ohne klickbare Links (nur Fließtext)"
else
ok "Quellen-Block mit $SOURCE_LINKS klickbaren Links"
fi
else
fail "Kein Quellen-Block gefunden (h2/span>Quellen fehlt)"
fi
# ── CHECK 14: Dachzeile vorhanden (div unter h1 mit border-bottom) ──
if echo "$BODY" | grep -qP '<div style="[^"]*border-bottom:2px solid #e0e0e0[^"]*"'; then
ok "Dachzeile vorhanden"
else
fail "Dachzeile fehlt (grauer Div-Balken unter h1)"
fi
# ── CHECK 15: Infobox vorhanden (f0f4f8 Hintergrund) ─────────────────
if echo "$BODY" | grep -q 'background:#f0f4f8'; then
ok "Infobox vorhanden"
else
fail "Infobox fehlt (Fakten-Tabelle mit #f0f4f8 Hintergrund)"
fi
# ── CHECK 16: Read-More NACH Einleitung, VOR erstem h2 (Joomla-Standard) ──
# Der Text VOR Read-More ist der Intro-Text für die Artikel-Übersicht.
# Alle h2-Abschnitte müssen HINTER dem Read-More stehen.
READMORE_LINE=$(grep -n 'system-readmore' "$FILE" | head -1 | cut -d: -f1)
FIRST_H2_LINE=$(grep -n '<h2>' "$FILE" | head -1 | cut -d: -f1 || echo "99999")
if [ "$FIRST_H2_LINE" = "99999" ]; then
warn "Keine <h2> im Artikel — Read-More-Position nicht prüfbar"
elif [ "$READMORE_LINE" -lt "$FIRST_H2_LINE" ]; then
ok "Read-More VOR erstem h2 — korrekt (Zeile $READMORE_LINE < $FIRST_H2_LINE)"
else
fail "Read-More NACH erstem h2 (Zeile $READMORE_LINE > $FIRST_H2_LINE). Joomla-Intro zu lang — h2-Inhalte verschwinden aus Übersicht!"
fi
# ── CHECK 17: JEDE Quelle im Quellen-Block hat <a href> ─────────────
# Extrahiere Quellen-Block (nach "Quellen" bis </ol>)
SRC_START=$(grep -n 'Quellen' "$FILE" | tail -1 | cut -d: -f1 || echo "")
if [ -n "$SRC_START" ]; then
SRC_LI=$(sed -n "$SRC_START,\$p" "$FILE" | grep -c '<li>' || true)
SRC_LINKED=$(sed -n "$SRC_START,\$p" "$FILE" | grep -c '<li>.*<a href=' || true)
if [ "$SRC_LI" -eq 0 ]; then
fail "Keine <li> im Quellen-Block"
elif [ "$SRC_LI" != "$SRC_LINKED" ]; then
UNLINKED=$((SRC_LI - SRC_LINKED))
fail "$UNLINKED von $SRC_LI Quellen ohne <a href> Link"
else
ok "Alle $SRC_LI Quellen verlinkt"
fi
fi
# ── CHECK 18: Recherchestand-Zeile vorhanden ─────────────────────────
if echo "$BODY" | grep -q 'Recherchestand:'; then
ok "Recherchestand-Zeile vorhanden"
else
fail "Recherchestand-Zeile fehlt"
fi
# ── SUMMARY ─────────────────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════"
if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
echo -e "${GREEN}✅ ALLE CHECKS BESTANDEN${NC}"
echo "═══════════════════════════════════════"
exit 0
elif [ $ERRORS -eq 0 ]; then
echo -e "${YELLOW}⚠️ $WARNINGS Warnung(en) — KEINE Fehler${NC}"
echo "═══════════════════════════════════════"
exit 0
else
echo -e "${RED}$ERRORS Fehler, $WARNINGS Warnungen${NC}"
echo "═══════════════════════════════════════"
exit 1
fi