feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne IT-Wissen verständlich. Auto-Modus (ein Cron-Job genügt für alles): - data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen (baseline.py: load_state/save_state/is_check_due/mark_check_run) - `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien) automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis; EIN kombinierter Report + EIN Alert - neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an) Klartext (scanner/plain.py): - Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden - Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten - Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt Komfort: - approve --all/--rebuild legt die Datei-Überwachung automatisch mit an (Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall - status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen - Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg. Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset- Baseline-Test. 165 Tests grün. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
54ff1a6060
commit
bed20db8bd
11 changed files with 856 additions and 202 deletions
|
|
@ -11,6 +11,8 @@ from email.message import EmailMessage
|
|||
|
||||
import requests
|
||||
|
||||
from .plain import ampel, klartext_befunde, was_tun
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_LEVEL_ORDER = {"green": 0, "yellow": 1, "red": 2}
|
||||
|
|
@ -43,35 +45,52 @@ def send_alert(report: dict, cfg: dict) -> None:
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _make_subject(report: dict) -> str:
|
||||
level = report.get("assessment", {}).get("level", "?").upper()
|
||||
level = report.get("assessment", {}).get("level", "green")
|
||||
target = report.get("target", "?")
|
||||
ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
||||
return f"[{level}] Integrity Alert: {target} — {ts}"
|
||||
symbol, _ = ampel(level)
|
||||
if level == "red":
|
||||
return f"{symbol} Ihre Website {target} braucht Aufmerksamkeit"
|
||||
if level == "yellow":
|
||||
return f"{symbol} Bitte einmal auf Ihrer Website {target} nachschauen"
|
||||
return f"{symbol} Ihre Website {target}: alles in Ordnung"
|
||||
|
||||
|
||||
def _format_body(report: dict) -> str:
|
||||
lines = [
|
||||
"=" * 60,
|
||||
"WEBSITE INTEGRITY ALERT",
|
||||
"=" * 60,
|
||||
f"Ziel: {report.get('target', '?')}",
|
||||
f"Zeitpunkt: {datetime.now(timezone.utc).isoformat()}",
|
||||
]
|
||||
|
||||
assessment = report.get("assessment", {})
|
||||
level = assessment.get("level", "?").upper()
|
||||
score = assessment.get("score", "?")
|
||||
level = assessment.get("level", "green")
|
||||
diff = report.get("diff", {})
|
||||
symbol, headline = ampel(level)
|
||||
|
||||
# ---- Klartext-Teil (für den Betreiber) ----
|
||||
lines = [
|
||||
f"Prüfbericht für Ihre Website: {report.get('target', '?')}",
|
||||
f"Zeitpunkt: {datetime.now(timezone.utc).strftime('%d.%m.%Y %H:%M Uhr')} (UTC)",
|
||||
"",
|
||||
f"{symbol} {headline}",
|
||||
"",
|
||||
"Was bedeutet das?",
|
||||
]
|
||||
for satz in klartext_befunde(
|
||||
diff, report.get("cloak_diff"), report.get("asset_diff"), report.get("ext_links")
|
||||
):
|
||||
lines.append(f" • {satz}")
|
||||
lines += ["", "Was sollten Sie tun?"]
|
||||
for schritt in was_tun(level):
|
||||
lines.append(f" → {schritt}")
|
||||
|
||||
# ---- Technischer Teil (für den Dienstleister) ----
|
||||
lines += [
|
||||
f"Level: {level}",
|
||||
f"Score: {score}",
|
||||
"",
|
||||
"=" * 60,
|
||||
"Technische Details (für Ihren Dienstleister)",
|
||||
"=" * 60,
|
||||
f"Level: {level.upper()} Score: {assessment.get('score', '?')}",
|
||||
"",
|
||||
"--- Gruende ---",
|
||||
]
|
||||
for reason in assessment.get("reasons", []):
|
||||
lines.append(f" * {reason}")
|
||||
|
||||
diff = report.get("diff", {})
|
||||
|
||||
_section(lines, "Neue externe Domains", diff.get("new_external_domains", []), "+")
|
||||
_section(lines, "Neue interne URLs", diff.get("new_internal_urls", []), "+")
|
||||
_section(lines, "Fehlende URLs", diff.get("missing_internal_urls", []), "-")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue