Add TeamPulse design spec

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Oliver Hofmann 2026-05-16 17:05:58 +02:00
commit 3ba945f283

View File

@ -0,0 +1,132 @@
# TeamPulse — Design-Dokument
**Datum:** 2026-05-16
**Status:** Zur Implementierung freigegeben
---
## Ziel
Ein Python-Script, das den Chat eines laufenden Microsoft Teams-Meetings überwacht, alle Personen erfasst, die in einem definierten Zeitfenster eine Nachricht gepostet haben, und ein Memo mit Name und E-Mail-Adresse der Teilnehmer erzeugt.
Ausgangslage: Nutzer ohne Admin-Rechte, kein Zugriff auf Microsoft Graph API / Azure AD App-Registrierung.
---
## Architektur
```
main.py
├── auth.py — Playwright-Session verwalten (Login, Persistenz)
├── monitor.py — Teams-Chat pollen, Trigger erkennen, Nachrichten sammeln
├── resolver.py — Sender-Name → E-Mail via Profilkarte (mit lokalem Cache)
└── memo.py — Markdown-Memo erzeugen und speichern
```
---
## Komponenten
### `auth.py`
- Startet Playwright mit persistentem User-Data-Verzeichnis (`~/.teampulse/session`)
- Beim ersten Start: sichtbares Browser-Fenster für manuellen SSO/MFA-Login
- Bei bekannter Session: headless, unsichtbar im Hintergrund
- Erkennt Session-Ablauf (Login-Redirect) und öffnet Browser-Fenster zur Re-Authentifizierung
- Liest beim Start den Display-Namen des eingeloggten Nutzers aus → wird als „Moderator" gespeichert
### `monitor.py`
- Öffnet Teams Web im Hintergrund-Browser
- Nutzer navigiert einmalig manuell zur Meeting-Chat-URL
- Pollt den Chat-DOM alle ~2 Sekunden auf neue Nachrichten
- Erkennt Trigger-Befehle (nur wenn gepostet vom eingeloggten Nutzer):
- `!start "Name des Vortragenden"` → startet Zeitfenster, speichert Vortragendenname
- `!stop` → beendet Zeitfenster
- Fallback: Enter-Taste in der Konsole als alternativer Trigger
- Sammelt während des Zeitfensters: `{ sender: str, timestamp: str }`
- Reconnect-Logik bei Seitenreload oder Verbindungsverlust (gesammelte Daten bleiben erhalten)
### `resolver.py`
- Iteriert nach `!stop` über alle eindeutigen Sender
- Prüft `~/.teampulse/cache.json` — bekannte Namen werden nicht erneut aufgelöst
- Für unbekannte Sender: klickt Profilkarte im Teams-Web → extrahiert E-Mail
- Fehlerbehandlung:
- Profilkarte nicht ladbar → `<nicht auflösbar>`
- Externer Gast → als Gast markieren: `<gast@extern.com>` oder `<externer Gast>`
- Cache wird bei jedem neuen Eintrag sofort geschrieben
### `memo.py`
- Filtert Vortragenden und Moderator aus der Teilnehmerliste
- Sortiert verbleibende Einträge nach E-Mail-Adresse
- Erzeugt Markdown-Ausgabe (siehe Format unten)
- Speichert Datei als `audit_YYYYMMDD_HHMM.md` im aktuellen Verzeichnis
- Gibt Inhalt zusätzlich in der Konsole aus
---
## Trigger-Verhalten
| Aktion | Chat-Befehl | Konsole |
|---|---|---|
| Zeitfenster starten | `!start "Anna Bauer"` | Enter drücken |
| Zeitfenster beenden | `!stop` | Enter drücken |
- Nur Befehle vom eingeloggten Nutzer werden als Trigger akzeptiert
- `!start` ohne Anführungszeichen oder ohne Namen → Warnung, Zeitfenster startet mit Platzhalter „Unbekannter Vortragender"
- Mehrfaches `!start` ohne `!stop` → zweites `!start` überschreibt das erste (kein Fehler)
---
## Ausgabeformat
```markdown
# Meeting Chat Audit
Zeitfenster: 10:03:42 10:47:15
## Teilnehmer am Vortrag von Anna Bauer (4)
_Moderator: Oliver Hofmann — Vortragender und Moderator ausgeschlossen_
- Klaus Huber <k.huber@company.com>
- Max Mustermann <m.mustermann@company.com>
- Sandra Vogel <s.vogel@company.com>
- Thomas Wolf <t.wolf@company.com>
_Sortiert nach E-Mail-Adresse. Erstellt: 2026-05-16 10:51_
```
- Keine Nachrichten im Zeitfenster → Hinweis „Keine Chat-Aktivität in diesem Zeitfenster"
- Nicht auflösbare E-Mails → Eintrag trotzdem aufgeführt, E-Mail als `<nicht auflösbar>`
---
## Technologie-Stack
| Komponente | Wahl |
|---|---|
| Sprache | Python 3.11+ |
| Browser-Automation | Playwright (sync API) |
| Cache | JSON-Datei (`~/.teampulse/cache.json`) |
| Ausgabe | Markdown |
| Plattform | Mac, Windows, Linux |
---
## Randfälle
| Situation | Verhalten |
|---|---|
| Session abgelaufen | Sichtbarer Browser für Re-Auth, danach wieder headless |
| Profilkarte nicht ladbar | `<nicht auflösbar>`, kein Absturz |
| Externer Gast | Als Gast markiert |
| `!start` ohne Namen | Warnung + Platzhalter |
| `!start` von anderem Nutzer | Ignoriert |
| Seitenreload während Monitoring | Automatischer Reconnect, keine Datenverlust |
| Keine Nachrichten im Fenster | Memo mit entsprechendem Hinweis |
---
## Nicht im Scope
- Graph API / Azure AD Integration
- GUI / Web-Dashboard
- Mehrere gleichzeitige Zeitfenster
- Historische Chat-Auswertung (vergangene Meetings)