Compare commits

...

2 Commits

Author SHA1 Message Date
hermes 54fd0b453f fix: intake.py format (ruff format) 2026-06-22 09:43:17 +00:00
hermes 564f9eb4d7 feat: Message-Intake-Service mit Kommando-Erkennung (closes #2) 2026-06-22 09:42:19 +00:00
2 changed files with 192 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
"""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 <spielname> — Spiel suchen\n"
"• bewerte <spielname> — 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 <spielname>",
}
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."
),
}
+89
View File
@@ -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"