feat: Unified Messaging System — kommunikation plugin, AI/Proactive/System participants, MessageSidebar, Rich Content Renderer
Phase 1: Backend plugin kommunikation (13 files, 10 tables, REST API, WebSocket, RBAC, DMS Bridge, Participant Registry, Mini-App Registry, Search Provider) Phase 2: AI plugins as participants (ai_assistant + ai_proactive dock as participants, heartbeat job) Phase 3: system_notif plugin (system events → chat messages, pinned System room) Phase 4: Frontend MessageSidebar (replaces AISidebar, same design, comm API client, WebSocket hook, commStore) Phase 5: Rich Content Block Renderer (11 components: Markdown, HTML, Image, Audio, Video, File, ActionCard, ContactCard, MiniApp, BlockRenderer) BasePlugin: added services property + _container in on_activate
This commit is contained in:
@@ -0,0 +1,624 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user