625 lines
25 KiB
Markdown
625 lines
25 KiB
Markdown
|
|
# Konzept: Unified Messaging System für LeoCRM
|
|||
|
|
|
|||
|
|
## Vision
|
|||
|
|
|
|||
|
|
Alle Kommunikation in LeoCRM — KI-Chat, Mitarbeiter-Chat, System-Benachrichtigungen, externe Messenger — läuft über **ein einheitliches Messaging-System**, das als Plugin-Architektur realisiert wird.
|
|||
|
|
|
|||
|
|
**Kernprinzip:** Alles ist ein Teilnehmer. Die KI ist ein Teilnehmer. Das System ist ein Teilnehmer. Ein WhatsApp-Gateway ist ein Teilnehmer. Es gibt keine Sonderbehandlung.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Plugin-Architektur
|
|||
|
|
|
|||
|
|
### Übersicht: Drei Plugin-Ebenen
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────┐
|
|||
|
|
│ Frontend (MessageSidebar) │
|
|||
|
|
│ Ein Feed, eine Konversations-Liste, ein Eingabefeld │
|
|||
|
|
└──────────────────────────┬──────────────────────────────────┘
|
|||
|
|
│ │ WebSocket │
|
|||
|
|
┌──────────────────────────┴──────────────────────────────────┐
|
|||
|
|
│ Plugin: kommunikation (Core) │
|
|||
|
|
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────┐ │
|
|||
|
|
│ │ conversations│ │ messages │ │ participants │ │
|
|||
|
|
│ │ + Räume │ │ + Rich Cont.│ │ + Registrierung │ │
|
|||
|
|
│ └─────────────┘ └──────────────┘ └────────────────────┘ │
|
|||
|
|
│ ┌──────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ Participant Registry (Hook) │ │
|
|||
|
|
│ │ Andockpunkt für: ai_assistant, system_notif, │ │
|
|||
|
|
│ │ whatsapp_gateway, telegram_gateway, ... │ │
|
|||
|
|
│ └──────────────────────────────────────────────────────┘ │
|
|||
|
|
└──────────────────────────┬──────────────────────────────────┘
|
|||
|
|
│ │ EventBus │
|
|||
|
|
┌──────────────────────────┴──────────────────────────────────┐
|
|||
|
|
│ Plugin: ai_assistant │ Plugin: system_notif │ Plugin: │
|
|||
|
|
│ dockt als Teilnehmer an │ dockt als Teilnehmer │ whatsapp │
|
|||
|
|
│ @KI → AI-Response │ Events → Messages │ Gateway │
|
|||
|
|
└─────────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 1. Plugin `kommunikation` (Core-Plugin)
|
|||
|
|
|
|||
|
|
**Verantwortung:** Chat-Infrastruktur — Konversationen, Nachrichten, Teilnehmer-Verwaltung, WebSocket, Rich Content Transport.
|
|||
|
|
|
|||
|
|
**Basiert auf:** AI Assistant Plugin (Sessions/Messages/Streaming) als Grundlage, erweitert um Multi-Teilnehmer und Rich Content.
|
|||
|
|
|
|||
|
|
**Manifest:**
|
|||
|
|
```python
|
|||
|
|
PluginManifest(
|
|||
|
|
name="kommunikation",
|
|||
|
|
version="1.0.0",
|
|||
|
|
display_name="Kommunikation",
|
|||
|
|
description="Unified Messaging: Chat, KI, System, Messenger",
|
|||
|
|
dependencies=[],
|
|||
|
|
routes=[
|
|||
|
|
PluginRouteDef(path="/api/v1/comm", module="...routes", router_attr="router"),
|
|||
|
|
],
|
|||
|
|
events=["message.received", "conversation.created", "participant.joined"],
|
|||
|
|
migrations=["0001_initial.sql"],
|
|||
|
|
permissions=["comm:read", "comm:write", "comm:manage"],
|
|||
|
|
is_core=True,
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Komponenten:**
|
|||
|
|
- `models.py` — Conversation, Message, Participant, MessageAttachment, MessageReaction
|
|||
|
|
- `schemas.py` — Pydantic-Schemas für API
|
|||
|
|
- `routes.py` — REST-API + WebSocket
|
|||
|
|
- `services.py` — Business Logic (Nachrichten senden, Konversationen verwalten)
|
|||
|
|
- `participant_registry.py` — Registrierungs-Interface für andere Plugins
|
|||
|
|
- `content_types.py` — Rich Content Type-Definitionen
|
|||
|
|
- `websocket_manager.py` — WebSocket-Verbindungs-Manager
|
|||
|
|
|
|||
|
|
### 2. Participant Registry (Andockpunkt)
|
|||
|
|
|
|||
|
|
Das `kommunikation` Plugin stellt eine **Participant Registry** bereit — ein Interface, über das sich andere Plugins als Teilnehmer registrieren.
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# In kommunikation/participant_registry.py
|
|||
|
|
|
|||
|
|
class ParticipantType(Enum):
|
|||
|
|
USER = "user"
|
|||
|
|
AI = "ai"
|
|||
|
|
SYSTEM = "system"
|
|||
|
|
WHATSAPP = "whatsapp"
|
|||
|
|
TELEGRAM = "telegram"
|
|||
|
|
EMAIL = "email"
|
|||
|
|
SLACK = "slack"
|
|||
|
|
# Erweiterbar...
|
|||
|
|
|
|||
|
|
class ParticipantHandler(ABC):
|
|||
|
|
"""Interface das Plugins implementieren um als Teilnehmer zu fungieren."""
|
|||
|
|
|
|||
|
|
@abstractmethod
|
|||
|
|
async def on_message_received(self, conversation_id, message, context) -> Message | None:
|
|||
|
|
"""Wird aufgerufen wenn eine neue Nachricht in einer Konversation
|
|||
|
|
ankommt, an der dieser Teilnehmer beteiligt ist.
|
|||
|
|
|
|||
|
|
Rückgabe: Optional eine neue Message (z.B. AI-Response).
|
|||
|
|
Für reine Leser (system) → return None.
|
|||
|
|
Für reaktive Teilnehmer (ai) → return Message(...).
|
|||
|
|
"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
@abstractmethod
|
|||
|
|
def get_participant_info(self) -> dict:
|
|||
|
|
"""Metadaten: name, avatar_url, display_name, capabilities."""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
class ParticipantRegistry:
|
|||
|
|
"""Global registry für Plugin-Teilnehmer."""
|
|||
|
|
|
|||
|
|
def register(self, participant_type: str, handler: ParticipantHandler) -> None:
|
|||
|
|
"""Plugin registriert sich als Teilnehmer-Typ."""
|
|||
|
|
|
|||
|
|
def get_handler(self, participant_type: str) -> ParticipantHandler | None:
|
|||
|
|
"""Handler für einen Teilnehmer-Typ abrufen."""
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Wie Plugins andocken:**
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# In ai_assistant/plugin.py on_activate():
|
|||
|
|
from app.plugins.builtins.kommunikation.participant_registry import get_registry
|
|||
|
|
|
|||
|
|
class AIAssistantPlugin(BasePlugin):
|
|||
|
|
async def on_activate(self, db, container, event_bus):
|
|||
|
|
await super().on_activate(db, container, event_bus)
|
|||
|
|
# Als AI-Teilnehmer registrieren
|
|||
|
|
registry = get_registry()
|
|||
|
|
registry.register("ai", AIParticipantHandler(self.services))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# In system_notif/plugin.py on_activate():
|
|||
|
|
class SystemNotificationPlugin(BasePlugin):
|
|||
|
|
async def on_activate(self, db, container, event_bus):
|
|||
|
|
await super().on_activate(db, container, event_bus)
|
|||
|
|
# Als System-Teilnehmer registrieren
|
|||
|
|
registry = get_registry()
|
|||
|
|
registry.register("system", SystemParticipantHandler(...))
|
|||
|
|
# Auf Events hören und Nachrichten erzeugen
|
|||
|
|
event_bus.subscribe("lead.created", self.on_lead_created)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Plugin `ai_assistant` (Anpassung)
|
|||
|
|
|
|||
|
|
Das bestehende AI Assistant Plugin wird angepasst:
|
|||
|
|
- Behält: Provider-Verwaltung, Modelle, Agents, Tools, Streaming
|
|||
|
|
- Neu: Implementiert `ParticipantHandler` und registriert sich bei `kommunikation`
|
|||
|
|
- Neu: Lauscht auf `message.received` Events → wenn `@KI` erwähnt wird oder Konversation AI als Teilnehmer hat → generiert Response
|
|||
|
|
- Alt: Eigene `AIChatSession` / `AIChatMessage` Tabellen bleiben für Abwärtskompatibilität, werden langfristig migriert
|
|||
|
|
- Neu: Schreibt Nachrichten in `kommunikation.messages` statt nur in eigene Tabellen
|
|||
|
|
|
|||
|
|
### 4. Plugin `system_notif` (Neu)
|
|||
|
|
|
|||
|
|
**Verantwortung:** System-Events in Nachrichten umwandeln.
|
|||
|
|
|
|||
|
|
- Registriert sich als `system` Teilnehmer
|
|||
|
|
- Hört auf EventBus-Events (`lead.created`, `contact.created`, `task.overdue`, ...)
|
|||
|
|
- Erzeugt Nachrichten in der System-Konversation des Users
|
|||
|
|
- Notifications haben `metadata.action_url` und `metadata.severity`
|
|||
|
|
- Bestehende Notification-Tabelle wird migriert
|
|||
|
|
|
|||
|
|
### 5. Messenger-Gateway Plugins (Später)
|
|||
|
|
|
|||
|
|
Jeder Messenger ist ein eigenes Plugin:
|
|||
|
|
- `whatsapp_gateway` — registriert sich als `whatsapp` Teilnehmer
|
|||
|
|
- `telegram_gateway` — registriert sich als `telegram` Teilnehmer
|
|||
|
|
- `email_gateway` — registriert sich als `email` Teilnehmer
|
|||
|
|
|
|||
|
|
Jedes implementiert `ParticipantHandler` und ggf. Webhook-Routes.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Datenmodell
|
|||
|
|
|
|||
|
|
### Tabelle `comm_conversations`
|
|||
|
|
```
|
|||
|
|
id UUID PK
|
|||
|
|
tenant_id UUID NOT NULL
|
|||
|
|
title TEXT NULL -- benannte Räume
|
|||
|
|
title_set_by UUID NULL -- user_id der den Titel gesetzt hat
|
|||
|
|
is_pinned BOOLEAN DEFAULT FALSE
|
|||
|
|
is_direct BOOLEAN DEFAULT FALSE -- 1:1 vs Gruppe
|
|||
|
|
created_by UUID NULL
|
|||
|
|
last_msg_at TIMESTAMP
|
|||
|
|
last_msg_preview TEXT NULL -- für Konversations-Liste
|
|||
|
|
last_msg_sender_type TEXT NULL -- für Icon in Liste
|
|||
|
|
metadata JSONB DEFAULT '{}' -- z.B. {"pinned_by": "user_id"}
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Tabelle `comm_participants`
|
|||
|
|
```
|
|||
|
|
id UUID PK
|
|||
|
|
conversation_id UUID FK → comm_conversations
|
|||
|
|
participant_id UUID NULL -- user_id (NULL für ai/system/gateways)
|
|||
|
|
participant_type TEXT NOT NULL -- 'user', 'ai', 'system', 'whatsapp', ...
|
|||
|
|
display_name TEXT NULL -- override (z.B. WhatsApp-Kontakt-Name)
|
|||
|
|
joined_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
left_at TIMESTAMP NULL
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Tabelle `comm_messages`
|
|||
|
|
```
|
|||
|
|
id UUID PK
|
|||
|
|
tenant_id UUID NOT NULL
|
|||
|
|
conversation_id UUID FK → comm_conversations
|
|||
|
|
sender_id UUID NULL -- user_id (NULL für ai/system/gateways)
|
|||
|
|
sender_type TEXT NOT NULL -- 'user', 'ai', 'system', 'whatsapp', ...
|
|||
|
|
content TEXT NOT NULL -- Text-Inhalt (Markdown)
|
|||
|
|
content_format TEXT DEFAULT 'text' -- 'text', 'markdown', 'html'
|
|||
|
|
metadata JSONB DEFAULT '{}' -- typ-spezifische Daten
|
|||
|
|
reply_to_id UUID NULL FK → comm_messages -- Thread-Antwort
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
read_at TIMESTAMP NULL
|
|||
|
|
edited_at TIMESTAMP NULL
|
|||
|
|
deleted_at TIMESTAMP NULL
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Tabelle `comm_message_attachments`
|
|||
|
|
```
|
|||
|
|
id UUID PK
|
|||
|
|
message_id UUID FK → comm_messages
|
|||
|
|
file_name TEXT NOT NULL
|
|||
|
|
file_path TEXT NOT NULL -- Pfad im DMS oder S3
|
|||
|
|
file_type TEXT NOT NULL -- MIME type
|
|||
|
|
file_size BIGINT
|
|||
|
|
thumbnail_path TEXT NULL -- für Bilder/Videos
|
|||
|
|
metadata JSONB DEFAULT '{}' -- z.B. {"width": 1920, "height": 1080}
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Tabelle `comm_message_reactions`
|
|||
|
|
```
|
|||
|
|
id UUID PK
|
|||
|
|
message_id UUID FK → comm_messages
|
|||
|
|
user_id UUID NOT NULL
|
|||
|
|
emoji TEXT NOT NULL
|
|||
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
UNIQUE(message_id, user_id, emoji)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Tabelle `comm_message_reads`
|
|||
|
|
```
|
|||
|
|
id UUID PK
|
|||
|
|
conversation_id UUID FK
|
|||
|
|
user_id UUID NOT NULL
|
|||
|
|
last_read_msg_id UUID FK → comm_messages
|
|||
|
|
last_read_at TIMESTAMP DEFAULT NOW()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Tabelle `comm_message_blocks` (Rich Content / Mini-Apps)
|
|||
|
|
```
|
|||
|
|
id UUID PK
|
|||
|
|
message_id UUID FK → comm_messages
|
|||
|
|
block_type TEXT NOT NULL -- 'file', 'image', 'audio', 'video',
|
|||
|
|
-- 'markdown', 'html', 'miniapp',
|
|||
|
|
-- 'action_card', 'contact_card', ...
|
|||
|
|
block_data JSONB NOT NULL -- typ-spezifische strukturierte Daten
|
|||
|
|
sort_order INT DEFAULT 0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Das ist der Schlüssel für Rich Content:**
|
|||
|
|
Eine Nachricht hat einen `content` (Text) plus beliebig viele `blocks` (strukturierte Elemente).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Rich Content Transport
|
|||
|
|
|
|||
|
|
### Block-Typen (erweiterbar durch Plugins)
|
|||
|
|
|
|||
|
|
| block_type | Beschreibung | block_data Beispiel |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `text` | Reiner Text (Fallback) | `{"text": "..."}` |
|
|||
|
|
| `markdown` | Markdown-Content | `{"markdown": "# Titel\n..."}` |
|
|||
|
|
| `html` | HTML-Content (sanitized) | `{"html": "<div>...</div>"}` |
|
|||
|
|
| `image` | Bild | `{"url": "...", "alt": "...", "width": 800}` |
|
|||
|
|
| `audio` | Audio-Datei | `{"url": "...", "duration": 120, "waveform": [...]}` |
|
|||
|
|
| `video` | Video-Datei | `{"url": "...", "duration": 60, "thumbnail": "..."}` |
|
|||
|
|
| `file` | Allgemeine Datei | `{"url": "...", "name": "...", "size": 1024}` |
|
|||
|
|
| `action_card` | Interaktive Karte mit Buttons | `{"title": "...", "body": "...", "actions": [{"label": "Öffnen", "url": "..."}]}` |
|
|||
|
|
| `contact_card` | Kontakt-Referenz | `{"contact_id": "...", "name": "..."}` |
|
|||
|
|
| `miniapp` | Eingebettete Mini-App | `{"app_id": "...", "config": {...}}` |
|
|||
|
|
|
|||
|
|
### Mini-App System
|
|||
|
|
|
|||
|
|
Mini-Apps sind kleine interaktive Komponenten, die **von Plugins registriert** und im Chat gerendert werden.
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Plugin registriert eine Mini-App:
|
|||
|
|
class MiniAppRegistry:
|
|||
|
|
def register(self, app_id: str, component: dict) -> None:
|
|||
|
|
"""Registriert eine Mini-App.
|
|||
|
|
|
|||
|
|
component = {
|
|||
|
|
'name': 'Lead Qualifier',
|
|||
|
|
'icon': 'clipboard',
|
|||
|
|
'render_schema': {...}, # JSON-Schema für Frontend
|
|||
|
|
'handler': async function # Backend-Handler
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Beispiel:** Ein Plugin `lead_qualifier` registriert eine Mini-App. Ein User schickt `/miniapp lead_qualifier` im Chat → eine Mini-App-Block wird erzeugt → Frontend rendert das interaktive Formular → Ergebnis wird als Nachricht zurückgeschrieben.
|
|||
|
|
|
|||
|
|
### Nachricht mit Rich Content — Beispiel
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": "...",
|
|||
|
|
"conversation_id": "...",
|
|||
|
|
"sender_type": "ai",
|
|||
|
|
"content": "Hier ist die Zusammenfassung der neuen Leads:",
|
|||
|
|
"blocks": [
|
|||
|
|
{
|
|||
|
|
"block_type": "markdown",
|
|||
|
|
"block_data": {
|
|||
|
|
"markdown": "## 3 neue Leads\n- **Acme Corp** — €50k potential\n- **Globex** — €20k potential\n- **Initech** — €10k potential"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"block_type": "action_card",
|
|||
|
|
"block_data": {
|
|||
|
|
"title": "Nächste Schritte",
|
|||
|
|
"body": "3 Leads warten auf Qualifizierung.",
|
|||
|
|
"actions": [
|
|||
|
|
{"label": "Alle öffnen", "action": "open_leads", "type": "primary"},
|
|||
|
|
{"label": "Ignorieren", "action": "dismiss", "type": "secondary"}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## API
|
|||
|
|
|
|||
|
|
### REST Endpoints
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
# Konversationen
|
|||
|
|
GET /api/v1/comm/conversations -- Liste (für aktuellen User)
|
|||
|
|
POST /api/v1/comm/conversations -- Neue Konversation
|
|||
|
|
GET /api/v1/comm/conversations/{id} -- Details + Teilnehmer
|
|||
|
|
PATCH /api/v1/comm/conversations/{id} -- Titel ändern, pinnen
|
|||
|
|
DELETE /api/v1/comm/conversations/{id} -- Löschen/Verlassen
|
|||
|
|
|
|||
|
|
# Teilnehmer
|
|||
|
|
POST /api/v1/comm/conversations/{id}/participants -- Teilnehmer hinzufügen
|
|||
|
|
DELETE /api/v1/comm/conversations/{id}/participants/{pid} -- Entfernen
|
|||
|
|
|
|||
|
|
# Nachrichten
|
|||
|
|
GET /api/v1/comm/conversations/{id}/messages -- Nachrichten (paginiert)
|
|||
|
|
POST /api/v1/comm/conversations/{id}/messages -- Nachricht senden
|
|||
|
|
PATCH /api/v1/comm/messages/{id} -- Bearbeiten/Lesen
|
|||
|
|
DELETE /api/v1/comm/messages/{id} -- Löschen
|
|||
|
|
|
|||
|
|
# Attachments
|
|||
|
|
POST /api/v1/comm/messages/{id}/attachments -- Datei hochladen
|
|||
|
|
GET /api/v1/comm/attachments/{id} -- Datei herunterladen
|
|||
|
|
|
|||
|
|
# Reaktionen
|
|||
|
|
POST /api/v1/comm/messages/{id}/reactions -- Reaktion hinzufügen
|
|||
|
|
DELETE /api/v1/comm/messages/{id}/reactions/{emoji} -- Reaktion entfernen
|
|||
|
|
|
|||
|
|
# Read State
|
|||
|
|
POST /api/v1/comm/conversations/{id}/read -- Als gelesen markieren
|
|||
|
|
|
|||
|
|
# Mini-Apps
|
|||
|
|
GET /api/v1/comm/miniapps -- Verfügbare Mini-Apps
|
|||
|
|
POST /api/v1/comm/conversations/{id}/miniapps -- Mini-App starten
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### WebSocket
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
WS /api/v1/comm/ws
|
|||
|
|
|
|||
|
|
# Client → Server
|
|||
|
|
{"type": "subscribe", "conversation_id": "..."}
|
|||
|
|
{"type": "typing", "conversation_id": "...", "is_typing": true}
|
|||
|
|
{"type": "ping"}
|
|||
|
|
|
|||
|
|
# Server → Client
|
|||
|
|
{"type": "message.new", "conversation_id": "...", "message": {...}}
|
|||
|
|
{"type": "message.updated", "message": {...}}
|
|||
|
|
{"type": "message.deleted", "id": "..."}
|
|||
|
|
{"type": "participant.joined", "conversation_id": "...", "participant": {...}}
|
|||
|
|
{"type": "participant.left", "conversation_id": "...", "participant_id": "..."}
|
|||
|
|
{"type": "typing", "conversation_id": "...", "user_id": "...", "is_typing": true}
|
|||
|
|
{"type": "reaction.added", "message_id": "...", "emoji": "👍", "user_id": "..."}
|
|||
|
|
{"type": "conversation.updated", "conversation": {...}}
|
|||
|
|
{"type": "pong"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## UI-Konzept
|
|||
|
|
|
|||
|
|
### MessageSidebar (ersetzt AISidebar)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌──────────────────────────────────────────┐
|
|||
|
|
│ Kommunikation [×] │
|
|||
|
|
├──────────────────────────────────────────┤
|
|||
|
|
│ 🔍 Suche... │
|
|||
|
|
├──────────────────────────────────────────┤
|
|||
|
|
│ 📌 Projekt Alpha │ ←angepinnt
|
|||
|
|
│ 🤖 KI: 3 Leads zusammengefasst... │
|
|||
|
|
│ ┌────────────────────────────────────┐ │
|
|||
|
|
│ │ Max: @KI fasse die Leads zusammen │ │
|
|||
|
|
│ │ 🤖 KI: 3 neue Leads, 2 aus... │ │
|
|||
|
|
│ │ Lisa: Super, danke! │ │
|
|||
|
|
│ │ ┌──────────────────────────────┐ │ │
|
|||
|
|
│ │ │ 📎 lead_report.pdf │ │ │
|
|||
|
|
│ │ │ ──────────────────────────── │ │ │
|
|||
|
|
│ │ │ ## 3 neue Leads │ │ │
|
|||
|
|
│ │ │ - **Acme Corp** — €50k │ │ │
|
|||
|
|
│ │ │ ──────────────────────────── │ │ │
|
|||
|
|
│ │ │ [Öffnen] [Archivieren] │ │ │
|
|||
|
|
│ │ └──────────────────────────────┘ │ │
|
|||
|
|
│ └────────────────────────────────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ 📌 Assistent (1:1 mit KI) │
|
|||
|
|
│ 🤖 47 Kontakte ohne Email... │
|
|||
|
|
│ │
|
|||
|
|
│ 👥 Sales Team │
|
|||
|
|
│ Max: Hat jemand die Q3-Zahlen? │
|
|||
|
|
│ │
|
|||
|
|
│ 🔔 System │
|
|||
|
|
│ 3 neue Leads importiert │
|
|||
|
|
│ │
|
|||
|
|
├──────────────────────────────────────────┤
|
|||
|
|
│ [📎] [Eingabefeld...] [Senden] │
|
|||
|
|
└──────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Konversations-Liste (links im Panel)
|
|||
|
|
- Angespinnte Konversationen oben (📌)
|
|||
|
|
- Ungelesene-Badge pro Konversation
|
|||
|
|
- Letzte Nachricht mit Sender-Icon (🤖/👤/🔔)
|
|||
|
|
- Klick → öffnet Konversation im Feed
|
|||
|
|
|
|||
|
|
### Feed (Mitte)
|
|||
|
|
- Chronologische Nachrichten
|
|||
|
|
- Sender-Icon + Name pro Nachricht
|
|||
|
|
- Rich Content Blocks inline gerendert
|
|||
|
|
- Action-Cards mit Buttons
|
|||
|
|
- Datei-Anhänge mit Vorschau
|
|||
|
|
- Reaktionen (Emoji-Bar beim Hover)
|
|||
|
|
- Lesebestätigung (gelesen-Häkchen)
|
|||
|
|
|
|||
|
|
### Eingabefeld (unten)
|
|||
|
|
- Text-Eingabe mit Markdown-Support
|
|||
|
|
- Datei-Anhang Button (📎)
|
|||
|
|
- Mini-App Picker (/command)
|
|||
|
|
- @Mention Support (@KI, @Max)
|
|||
|
|
- Senden-Button
|
|||
|
|
- Kontextsensitiv: in System-Konversation → kein Eingabefeld
|
|||
|
|
|
|||
|
|
### Teilnehmer-Info
|
|||
|
|
- In Konversations-Header: Avatare aller Teilnehmer
|
|||
|
|
- Klick auf Avatar → Info-Popover
|
|||
|
|
- KI-Teilnehmer: zeigt Modell/Agent
|
|||
|
|
- System-Teilnehmer: zeigt Quelle
|
|||
|
|
|
|||
|
|
### Räume
|
|||
|
|
- Konversationen können benannt werden (Titel editierbar)
|
|||
|
|
- Anpinnen möglich (📌)
|
|||
|
|
- Gruppierung durch Titel, nicht durch spezielle Raum-Logik
|
|||
|
|
- Ein "Raum" ist einfach eine benannte Konversation
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## EventBus Integration
|
|||
|
|
|
|||
|
|
### Events vom kommunikation Plugin
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
message.received → {conversation_id, message, sender_type}
|
|||
|
|
message.sent → {conversation_id, message}
|
|||
|
|
conversation.created → {conversation_id, participants, created_by}
|
|||
|
|
participant.joined → {conversation_id, participant_type, participant_id}
|
|||
|
|
participant.left → {conversation_id, participant_id}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Events die andere Plugins hören
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
# ai_assistant hört auf:
|
|||
|
|
message.received → prüft ob @KI erwähnt oder AI Teilnehmer → generiert Response
|
|||
|
|
|
|||
|
|
# system_notif hört auf (vom Core-System):
|
|||
|
|
lead.created → erzeugt System-Nachricht
|
|||
|
|
contact.created → erzeugt System-Nachricht
|
|||
|
|
task.overdue → erzeugt System-Nachricht
|
|||
|
|
|
|||
|
|
# whatsapp_gateway hört auf:
|
|||
|
|
message.received → wenn Konversation WhatsApp-Teilnehmer hat → sende extern
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Migration
|
|||
|
|
|
|||
|
|
### Phase 1: Backend — Plugin `kommunikation`
|
|||
|
|
1. Neue Tabellen: `comm_conversations`, `comm_participants`, `comm_messages`, `comm_message_attachments`, `comm_message_blocks`, `comm_message_reactions`, `comm_message_reads`
|
|||
|
|
2. Participant Registry Interface
|
|||
|
|
3. REST-API + WebSocket
|
|||
|
|
4. Rich Content Block System
|
|||
|
|
5. Mini-App Registry Interface
|
|||
|
|
|
|||
|
|
### Phase 2: Backend — Plugin `ai_assistant` anpassen
|
|||
|
|
1. `ParticipantHandler` implementieren
|
|||
|
|
2. Bei `kommunikation` registrieren
|
|||
|
|
3. Auf `message.received` hören → AI-Response generieren
|
|||
|
|
4. Streaming-Responses über WebSocket pushen
|
|||
|
|
5. Alte `AIChatSession`/`AIChatMessage` behalten für Abwärtskompatibilität
|
|||
|
|
|
|||
|
|
### Phase 3: Backend — Plugin `system_notif` (neu)
|
|||
|
|
1. `ParticipantHandler` implementieren
|
|||
|
|
2. System-Events → Nachrichten in System-Konversation
|
|||
|
|
3. Bestehende Notifications migrieren
|
|||
|
|
4. Action-URLs als `action_card` Blocks
|
|||
|
|
|
|||
|
|
### Phase 4: Frontend — MessageSidebar
|
|||
|
|
1. AISidebar → MessageSidebar umbauen
|
|||
|
|
2. Konversations-Liste mit Pinning
|
|||
|
|
3. Unified Feed mit Rich Content Rendering
|
|||
|
|
4. Eingabefeld mit Datei-Upload + @Mention
|
|||
|
|
5. WebSocket-Verbindung
|
|||
|
|
6. Mini-App Rendering Framework
|
|||
|
|
|
|||
|
|
### Phase 5: Frontend — Rich Content Renderer
|
|||
|
|
1. Block-Renderer: Markdown, HTML, Image, Audio, Video, File
|
|||
|
|
2. Action-Card Renderer mit Button-Handler
|
|||
|
|
3. Mini-App Renderer (Plugin-basiert)
|
|||
|
|
4. Contact-Card, Lead-Card, etc.
|
|||
|
|
|
|||
|
|
### Phase 6: Messenger-Gateway Plugins (später)
|
|||
|
|
1. `whatsapp_gateway` Plugin
|
|||
|
|
2. `telegram_gateway` Plugin
|
|||
|
|
3. `email_gateway` Plugin
|
|||
|
|
4. Jeweils: ParticipantHandler + Webhook-Routes + Gateway-Adapter
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Technische Entscheidungen
|
|||
|
|
|
|||
|
|
### WebSocket vs Polling
|
|||
|
|
**WebSocket** — eine Verbindung pro User, pusht alle Konversationen.
|
|||
|
|
Grund: Real-time ist essenziell für Chat, und eine Verbindung für alles ist effizienter als Multiple Polling.
|
|||
|
|
|
|||
|
|
### Rich Content: Blocks vs Inline
|
|||
|
|
**Blocks** — separate Tabelle `comm_message_blocks` mit `block_type` + `block_data`.
|
|||
|
|
Grund: Erweiterbar durch Plugins, strukturiert, frontend kann unbekannte Typen graceful ignorieren.
|
|||
|
|
|
|||
|
|
### Mini-Apps: Plugin-basiert
|
|||
|
|
**Registry Pattern** — Plugins registrieren Mini-Apps bei `kommunikation`.
|
|||
|
|
Grund: Plugins können eigene Mini-Apps mitbringen, Frontend rendert sie dynamisch.
|
|||
|
|
|
|||
|
|
### Räume: Keine separate Tabelle
|
|||
|
|
**Titel + Pinning** — eine Konversation mit Titel ist ein Raum.
|
|||
|
|
Grund: Minimalistisch, keine zusätzliche Komplexität, flexibel.
|
|||
|
|
|
|||
|
|
### @Mention Detection
|
|||
|
|
**Im Backend** — `message.received` Event enthält geparste mentions.
|
|||
|
|
Grund: Zentrale Logik, alle Teilnehmer-Plugins bekommen saubere Daten.
|
|||
|
|
|
|||
|
|
### Abwärtskompatibilität
|
|||
|
|
**Alte Tabellen behalten** — `ai_chat_sessions`, `ai_chat_messages`, `ai_conversations`, `ai_messages` bleiben erhalten.
|
|||
|
|
Grund: Bestehende Daten gehen nicht verloren, Migration schrittweise.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Offene Fragen
|
|||
|
|
|
|||
|
|
1. **Soll `kommunikation` das bestehende AI Copilot System (AIConversation/AIMessage) ersetzen oder parallel laufen?**
|
|||
|
|
- Vorschlag: Parallel, langfristig migrieren
|
|||
|
|
|
|||
|
|
2. **Datei-Speicherung:** DMS-Plugin nutzen oder eigener Speicher für Attachments?
|
|||
|
|
- Vorschlag: DMS-Integration, `file_path` verweist auf DMS-Dokument
|
|||
|
|
|
|||
|
|
3. **Berechtigungen:** Wer darf Konversationen erstellen? Wer darf Teilnehmer hinzufügen?
|
|||
|
|
- Vorschlag: `comm:write` für erstellen, `comm:manage` für Teilnehmer verwalten
|
|||
|
|
|
|||
|
|
4. **Gruppen-Chat-Limit:** Maximale Anzahl Teilnehmer?
|
|||
|
|
- Vorschlag: Kein Limit, Performance-Test später
|
|||
|
|
|
|||
|
|
5. **Nachrichten-Historie:** Endlos oder Paginierung mit Lazy-Loading?
|
|||
|
|
- Vorschlag: Paginierung (50 pro Seite), Lazy-Load beim Scrollen
|
|||
|
|
|
|||
|
|
6. **Suche:** Über alle Konversationen? Global mit unified_search Plugin?
|
|||
|
|
- Vorschlag: Ja, `unified_search` Provider für `kommunikation`
|
|||
|
|
|
|||
|
|
7. **Push-Notifications:** Browser-Notifications bei neuen Nachrichten?
|
|||
|
|
- Vorschlag: Ja, über Notification API + Service Worker
|
|||
|
|
|
|||
|
|
8. **Verschlüsselung:** E2E für bestimmte Konversationen?
|
|||
|
|
- Vorschlag: Nein in Phase 1, später evaluieren
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Zusammenfassung
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Ein Plugin (kommunikation) → Chat-Infrastruktur + Rich Content + WebSocket
|
|||
|
|
Ein Interface (ParticipantHandler) → Plugins docken als Teilnehmer an
|
|||
|
|
Ein Datenmodell (3+Tabellen) → Konversationen, Teilnehmer, Nachrichten + Blocks
|
|||
|
|
Eine UI (MessageSidebar) → Ein Feed, eine Liste, ein Eingabefeld
|
|||
|
|
Eine WebSocket → Real-time für alles
|
|||
|
|
Ein EventBus → Plugins reagieren auf Nachrichten
|
|||
|
|
|
|||
|
|
KI = Teilnehmer → @KI in jedem Chat
|
|||
|
|
System = Teilnehmer → Notifications als Nachrichten
|
|||
|
|
WhatsApp = Teilnehmer → Externe Messenger andocken
|
|||
|
|
Mini-Apps = Plugin-Blocks → Erweiterbar im Chat
|
|||
|
|
Räume = Benannte Chats → Titel + Pinning
|
|||
|
|
```
|