From 564f9eb4d7442689fe341613b2ec6a227cf4c3e1 Mon Sep 17 00:00:00 2001 From: hermes Date: Mon, 22 Jun 2026 09:42:19 +0000 Subject: [PATCH] feat: Message-Intake-Service mit Kommando-Erkennung (closes #2) --- src/services/intake.py | 104 +++++++++++++++++++++++++++++++++++++++++ tests/test_intake.py | 89 +++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 src/services/intake.py create mode 100644 tests/test_intake.py diff --git a/src/services/intake.py b/src/services/intake.py new file mode 100644 index 0000000..09d7f16 --- /dev/null +++ b/src/services/intake.py @@ -0,0 +1,104 @@ +"""Message-Intake-Service.""" + +from __future__ import annotations + +from enum import StrEnum + +from src.models.submission import Submission + + +class KommandoTyp(StrEnum): + """Bekannte Benutzer-Kommandos.""" + + SPIEL_SUCHEN = "spiel_suchen" + SPIEL_BEWERTEN = "spiel_bewerten" + HILFE = "hilfe" + UNBEKANNT = "unbekannt" + + +def erkenne_kommando(text: str) -> KommandoTyp: + """Erkennt das Kommando aus dem Nachrichtentext. + + Args: + text: Der Nachrichtentext. + + Returns: + Das erkannte KommandoTyp-Enum. + """ + text_lower = text.lower().strip() + + if text_lower.startswith("suche") or text_lower.startswith("finde"): + return KommandoTyp.SPIEL_SUCHEN + if text_lower.startswith("bewerte") or text_lower.startswith("bewertung"): + return KommandoTyp.SPIEL_BEWERTEN + if text_lower in ("hilfe", "help", "?", "commands"): + return KommandoTyp.HILFE + + return KommandoTyp.UNBEKANNT + + +def extrahiere_suchbegriff(text: str) -> str | None: + """Extrahiert den Suchbegriff aus einer Such-Nachricht. + + Args: + text: Der Nachrichtentext (z.B. "suche Catan"). + + Returns: + Der Suchbegriff ohne Kommando-Präfix, oder None. + """ + for praefix in ("suche ", "finde "): + if text.lower().startswith(praefix): + begriff = text[len(praefix) :].strip() + if begriff: + return begriff + return None + + +def verarbeite_submission(submission: Submission) -> dict[str, object]: + """Verarbeitet eine Submission und gibt eine Antwort-Struktur zurück. + + Args: + submission: Die eingegangene Submission. + + Returns: + Ein Dict mit 'antwort' und 'kommando'-Keys. + """ + kommando = erkenne_kommando(submission.text) + + if kommando == KommandoTyp.HILFE: + return { + "kommando": kommando.value, + "antwort": ( + "📋 Verfügbare Kommandos:\n" + "• suche — Spiel suchen\n" + "• bewerte — Spiel bewerten\n" + "• hilfe — Diese Hilfe anzeigen" + ), + } + + if kommando == KommandoTyp.SPIEL_SUCHEN: + suchbegriff = extrahiere_suchbegriff(submission.text) + if suchbegriff: + return { + "kommando": kommando.value, + "antwort": f"🔍 Suche nach '{suchbegriff}'...", + "suchbegriff": suchbegriff, + } + return { + "kommando": kommando.value, + "antwort": "❌ Bitte gib einen Suchbegriff an: suche ", + } + + if kommando == KommandoTyp.SPIEL_BEWERTEN: + return { + "kommando": kommando.value, + "antwort": "⭐ Bewertungsmodus aktiviert. Welches Spiel möchtest du bewerten?", + } + + return { + "kommando": KommandoTyp.UNBEKANNT.value, + "antwort": ( + "🤔 Das habe ich nicht verstanden. Schreib 'hilfe' " + "für eine Liste der Kommandos." + ), + } diff --git a/tests/test_intake.py b/tests/test_intake.py new file mode 100644 index 0000000..e99777a --- /dev/null +++ b/tests/test_intake.py @@ -0,0 +1,89 @@ +"""Tests für Message-Intake-Service.""" + +from unittest.mock import MagicMock + +from src.services.intake import ( + KommandoTyp, + erkenne_kommando, + extrahiere_suchbegriff, + verarbeite_submission, +) + + +class TestErkenneKommando: + """Tests für erkenne_kommando.""" + + def test_suche_catanspielen_ergibt_spiel_suchen(self) -> None: + """'suche Catan' → KommandoTyp.SPIEL_SUCHEN.""" + assert erkenne_kommando("suche Catan") == KommandoTyp.SPIEL_SUCHEN + + def test_suche_ohne_begriff_ergibt_spiel_suchen(self) -> None: + """'suche' → KommandoTyp.SPIEL_SUCHEN.""" + assert erkenne_kommando("suche") == KommandoTyp.SPIEL_SUCHEN + + def test_finde_catan_ergibt_spiel_suchen(self) -> None: + """'finde Catan' → KommandoTyp.SPIEL_SUCHEN.""" + assert erkenne_kommando("finde Catan") == KommandoTyp.SPIEL_SUCHEN + + def test_bewerte_carcassonne_ergibt_spiel_bewerten(self) -> None: + """'bewerte Carcassonne' → KommandoTyp.SPIEL_BEWERTEN.""" + assert erkenne_kommando("bewerte Carcassonne") == KommandoTyp.SPIEL_BEWERTEN + + def test_hilfe_ergibt_hilfe(self) -> None: + """'hilfe' → KommandoTyp.HILFE.""" + assert erkenne_kommando("hilfe") == KommandoTyp.HILFE + + def test_unknown_ertext_ergibt_unbekannt(self) -> None: + """'blabla' → KommandoTyp.UNBEKANNT.""" + assert erkenne_kommando("blabla") == KommandoTyp.UNBEKANNT + + +class TestExtrahiereSuchbegriff: + """Tests für extrahiere_suchbegriff.""" + + def test_suche_catans_gibt_suchbegriff(self) -> None: + """'suche Catan' → extrahiert 'Catan'.""" + assert extrahiere_suchbegriff("suche Catan") == "Catan" + + def test_suche_ohne_begriff_gibt_none(self) -> None: + """'suche' → None.""" + assert extrahiere_suchbegriff("suche") is None + + def test_finde_monopoly_gibt_suchbegriff(self) -> None: + """'finde Monopoly' → extrahiert 'Monopoly'.""" + assert extrahiere_suchbegriff("finde Monopoly") == "Monopoly" + + +class TestVerarbeiteSubmission: + """Tests für verarbeite_submission.""" + + def test_verarbeite_submission_mit_hilfe(self) -> None: + """verarbeite_submission mit 'hilfe' → Antwort enthält Hilfe-Text.""" + mock_submission = MagicMock() + mock_submission.text = "hilfe" + + result = verarbeite_submission(mock_submission) + + assert result["kommando"] == "hilfe" + assert "Verfügbare Kommandos" in result["antwort"] + assert "suche" in result["antwort"] + + def test_verarbeite_submission_mit_suche(self) -> None: + """verarbeite_submission mit 'suche Catan' → Antowrt enthält Suchbegriff.""" + mock_submission = MagicMock() + mock_submission.text = "suche Catan" + + result = verarbeite_submission(mock_submission) + + assert result["kommando"] == "spiel_suchen" + assert result["suchbegriff"] == "Catan" + assert "Catan" in result["antwort"] + + def test_verarbeite_submission_mit_unbekanntem_text(self) -> None: + """verarbeite_submission mit unbekanntem Text → UNBEKANNT.""" + mock_submission = MagicMock() + mock_submission.text = "hallo wie gehts" + + result = verarbeite_submission(mock_submission) + + assert result["kommando"] == "unbekannt"