feat: Telegram-Webhook mit JSON-Validierung und Queue-Enqueue

This commit is contained in:
Hermes Agent
2026-06-22 06:37:31 +02:00
parent e382201878
commit 5a72f9ec80
3 changed files with 155 additions and 0 deletions
View File
+44
View File
@@ -0,0 +1,44 @@
"""Telegram-Webhook Blueprint."""
from flask import Blueprint, current_app, request
from pydantic import ValidationError
from src.models.submission import Submission
telegram = Blueprint("telegram", __name__)
@telegram.route("/webhook/telegram", methods=["POST"])
def webhook_telegram() -> tuple[dict[str, str], int]:
"""Empfängt Telegram-Updates via Webhook und reiht sie in die Queue ein."""
data = request.get_json(silent=True)
if data is None:
return {"error": "Ungültiges oder fehlendes JSON"}, 400
message = data.get("message")
if not isinstance(message, dict):
return {"error": "Feld 'message' fehlt oder ist ungültig"}, 400
message_id = message.get("message_id")
if not message_id:
return {"error": "Feld 'message_id' fehlt"}, 400
text = message.get("text")
if not text:
return {"error": "Feld 'text' fehlt oder ist leer"}, 400
chat = message.get("chat", {})
chat_id = chat.get("id") if isinstance(chat, dict) else None
try:
submission = Submission(
message_id=str(message_id),
plattform="telegram",
absender=str(chat_id) if chat_id else "unknown",
text=str(text),
)
except ValidationError:
return {"error": "Validierungsfehler"}, 400
current_app.queue_handler.enqueue(submission) # type: ignore[attr-defined]
return {"status": "ok"}, 200
+111
View File
@@ -0,0 +1,111 @@
"""Tests für den Telegram-Webhook Blueprint."""
from __future__ import annotations
import fakeredis
from flask import Flask
from flask.testing import FlaskClient
from src.app import create_app
from src.queue.handler import QueueHandler
from src.routes.telegram import telegram
def _setup_app_with_fakeredis() -> Flask:
"""Erstellt eine Flask-App mit fakeredis-QueueHandler und registriert den Telegram-Blueprint."""
app = create_app()
fake_redis = fakeredis.FakeRedis(decode_responses=True)
qh = QueueHandler.__new__(QueueHandler)
qh.redis = fake_redis
qh.stream_key = "bsn:submissions"
qh.processed_set = "bsn:processed"
qh.stream_ids_hash = "bsn:stream_ids"
qh.group_name = "bsn-workers"
qh._ensure_consumer_group()
app.queue_handler = qh # type: ignore[attr-defined]
app.register_blueprint(telegram)
return app
def test_valid_telegram_payload_returns_200() -> None:
"""POST /webhook/telegram mit gültigem Payload → 200."""
app = _setup_app_with_fakeredis()
client: FlaskClient = app.test_client()
payload = {
"update_id": 123456789,
"message": {
"message_id": 100,
"from": {
"id": 987654321,
"first_name": "TestUser",
},
"chat": {
"id": 987654321,
"first_name": "TestUser",
"type": "private",
},
"date": 1719000000,
"text": "Hallo BSN-Bot!",
},
}
response = client.post("/webhook/telegram", json=payload)
assert response.status_code == 200
data = response.get_json()
assert data is not None
assert data["status"] == "ok"
def test_missing_json_returns_400() -> None:
"""POST /webhook/telegram ohne JSON-Body → 400."""
app = _setup_app_with_fakeredis()
client: FlaskClient = app.test_client()
response = client.post("/webhook/telegram", data="not json", content_type="text/plain")
assert response.status_code == 400
data = response.get_json()
assert data is not None
assert "error" in data
def test_missing_message_id_returns_400() -> None:
"""POST /webhook/telegram ohne message_id → 400."""
app = _setup_app_with_fakeredis()
client: FlaskClient = app.test_client()
payload = {
"message": {
"chat": {"id": 123},
"text": "Hallo",
}
}
response = client.post("/webhook/telegram", json=payload)
assert response.status_code == 400
data = response.get_json()
assert data is not None
assert "message_id" in data["error"]
def test_empty_text_returns_400() -> None:
"""POST /webhook/telegram mit leerem text → 400 (min_length=1 ValidationError)."""
app = _setup_app_with_fakeredis()
client: FlaskClient = app.test_client()
payload = {
"message": {
"message_id": 200,
"chat": {"id": 456},
"text": "",
}
}
response = client.post("/webhook/telegram", json=payload)
assert response.status_code == 400
data = response.get_json()
assert data is not None
assert "error" in data