Plugin benachrichtigung: E-Mail, Telegram, In-App per Adapter-Muster

- Adapter-Pattern mit drei Kanaelen: E-Mail (SMTP per Env, TLS starttls/ssl,
  Dev-Fallback: Protokoll), Telegram (Bot-API, Token per Env, Chat-ID pro
  Benutzer), In-App (persistente Nachrichten mit Unread-Counter und
  'Alle als gelesen markieren')
- Pro Benutzer Kanal-Praeferenzen (Einstellungsseite, mehrere Kanaele
  gleichzeitig) plus eigene Kontakt-Tabelle (E-Mail-Adresse, Chat-ID);
  Kern und users-Tabelle unveraendert
- Oeffentliche Plugin-API: await send_notification(user, titel, text,
  kategorie) - andere Plugins holen das Plugin ueber app.state.registry
- Eigene Migration (0001_tabellen), eigene Routen/Templates, deutsche UI
- Tests: Adapter-Auswahl nach Praeferenz, In-App-Persistenz, Dev-Log-
  Adapter, SMTP-Versand (gemockt), Telegram-API-Aufruf, Einstellungsseite
- README: Plugin-Doku + neue Env-Variablen; docker-compose: Platzhalter
This commit is contained in:
Flo Hartmann
2026-08-21 19:00:06 +00:00
parent 7edf80d01c
commit f51732bff1
9 changed files with 1286 additions and 28 deletions

View File

@@ -0,0 +1,53 @@
"""Plugin-Konfiguration über Umgebungsvariablen.
Der Plugin-Kontrakt verbietet Fachlogik im Kern — deshalb liest dieses
Plugin seine Kanal-Konfiguration selbst aus der Umgebung, statt Felder in
`redaktionskern.config.Settings` zu ergänzen.
"""
from __future__ import annotations
import os
from dataclasses import dataclass
def _env(name: str, standard: str = "") -> str:
wert = os.environ.get(name)
return wert.strip() if wert else standard
@dataclass(frozen=True)
class Konfiguration:
"""Zustellkanal-Einstellungen der Installation (nicht pro Benutzer)."""
smtp_host: str
smtp_port: int
smtp_benutzer: str
smtp_passwort: str
smtp_absender: str
#: "starttls" (Standard), "ssl" oder "keine"
smtp_tls: str
telegram_bot_token: str
@property
def smtp_konfiguriert(self) -> bool:
return bool(self.smtp_host)
@property
def telegram_konfiguriert(self) -> bool:
return bool(self.telegram_bot_token)
def aus_umgebung() -> Konfiguration:
benutzer = _env("SPIELE_SMTP_BENUTZER")
tls = _env("SPIELE_SMTP_TLS", "starttls").lower()
if tls not in ("starttls", "ssl", "keine"):
tls = "starttls"
return Konfiguration(
smtp_host=_env("SPIELE_SMTP_HOST"),
smtp_port=int(_env("SPIELE_SMTP_PORT", "587")),
smtp_benutzer=benutzer,
smtp_passwort=_env("SPIELE_SMTP_PASSWORT"),
smtp_absender=_env("SPIELE_SMTP_ABSENDER") or benutzer or "spiele-redaktion@localhost",
smtp_tls=tls,
telegram_bot_token=_env("SPIELE_TELEGRAM_BOT_TOKEN"),
)