cms.c2sgmbh/prompts/Community-Management-Roadmap-Extended.md
Martin Porwoll 77f70876f4 chore: add Claude Code config, prompts, and tenant setup scripts
- Add .claude/ configuration (agents, commands, hooks, get-shit-done workflows)
- Add prompts/ directory with development planning documents
- Add scripts/setup-tenants/ with tenant configuration
- Add docs/screenshots/
- Remove obsolete phase2.2-corrections-report.md
- Update pnpm-lock.yaml
- Update detect-secrets.sh to ignore setup.sh (env var usage, not secrets)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 10:18:05 +00:00

1382 lines
42 KiB
Markdown

# Community Management System - Roadmap 2.3 bis 3.0 (Erweitert)
**Version:** 2.0 (Erweitert mit Facebook-Integration)
**Datum:** 16. Januar 2026
**Erstellt von:** Dev-KI nach Analyse des Konzept-Prompts
---
## Analyse des Original-Prompts
### Umsetzbarkeit in aktueller Implementierung
| Komponente | Status | Bewertung |
|------------|--------|-----------|
| SocialPlatforms Collection | ✅ Existiert | Instagram, Facebook bereits definiert |
| SocialAccounts Collection | ✅ Existiert | OAuth-Credentials-Struktur vorhanden |
| CommunityInteractions | ✅ Existiert | Plattform-agnostisch, erweiterbar |
| Rules Engine | ✅ Existiert | Basis für Auto-Reply vorhanden |
| ClaudeAnalysisService | ✅ Existiert | Wiederverwendbar für alle Plattformen |
| ClaudeReplyService | ✅ Existiert | Wiederverwendbar für alle Plattformen |
| E-Mail Service | ✅ Existiert | Für Reports nutzbar |
| YouTubeClient Pattern | ✅ Vorlage | Replizierbar für Meta/TikTok |
### Identifizierte Synergien
1. **Facebook + Instagram**: Beide nutzen Meta Graph API → Gemeinsame Auth-Basis
2. **CommentsSyncService**: Pattern kann für alle Plattformen verwendet werden
3. **Rules Engine → Auto-Reply**: Natürliche Erweiterung der bestehenden Logik
---
## Restrukturierte Phasen (Logisch aufgeteilt)
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ IMPLEMENTIERUNGSREIHENFOLGE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Phase 2.3a Phase 2.3b Phase 2.3c Phase 2.4 Phase 2.5a Phase 2.5b │
│ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │ Meta │───▶│Facebook│───▶│Insta- │───▶│TikTok │──▶│Auto- │──▶│Auto- │ │
│ │ OAuth │ │ Integr.│ │ gram │ │ │ │Reply │ │Reply │ │
│ └───────┘ └───────┘ └───────┘ └───────┘ │Service│ │ UI │ │
│ └───────┘ └───────┘ │
│ │ │ │
│ ▼ ▼ │
│ Phase 3.0a Phase 3.0b │
│ ┌───────────┐ ┌───────────┐ │
│ │ Scheduled │ │ Real-time │ │
│ │ Reports │ │ Updates │ │
│ └───────────┘ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Phase 2.3a: Meta OAuth & Basis-Infrastruktur
**Dauer:** 1-2 Tage
**Abhängigkeiten:** Meta Business Suite Zugang, App-Konfiguration
### Ziel
Gemeinsame OAuth-Infrastruktur für Facebook und Instagram (beide nutzen Meta Graph API).
### Voraussetzungen
- Facebook Developer Account
- Meta Business Suite mit verbundener Facebook Page
- Instagram Business Account (mit Facebook Page verknüpft)
- App in Meta for Developers erstellt
### Deliverables
#### 1. Meta OAuth Flow
```
src/app/(payload)/api/auth/meta/
├── route.ts # OAuth initiate
└── callback/route.ts # OAuth callback (für FB + IG)
```
**OAuth Configuration:**
```typescript
// src/lib/integrations/meta/oauth.ts
const META_OAUTH_CONFIG = {
authorizationUrl: 'https://www.facebook.com/v19.0/dialog/oauth',
tokenUrl: 'https://graph.facebook.com/v19.0/oauth/access_token',
scopes: [
// Facebook
'pages_show_list',
'pages_read_engagement',
'pages_manage_posts',
'pages_read_user_content',
'pages_manage_engagement',
// Instagram
'instagram_basic',
'instagram_manage_comments',
'instagram_manage_messages',
// Business
'business_management',
],
}
export async function getAuthUrl(state: string): Promise<string>
export async function exchangeCodeForTokens(code: string): Promise<MetaTokens>
export async function refreshAccessToken(token: string): Promise<MetaTokens>
export async function getLongLivedToken(shortLivedToken: string): Promise<string>
```
#### 2. Meta Base Client
```typescript
// src/lib/integrations/meta/MetaBaseClient.ts
export class MetaBaseClient {
protected accessToken: string
protected apiVersion: string = 'v19.0'
protected baseUrl: string = 'https://graph.facebook.com'
constructor(accessToken: string)
protected async request<T>(
endpoint: string,
method?: 'GET' | 'POST' | 'DELETE',
params?: Record<string, any>
): Promise<T>
async getMe(): Promise<MetaUser>
async getPages(): Promise<FacebookPage[]>
async getInstagramAccounts(pageId: string): Promise<InstagramAccount[]>
}
```
#### 3. Types
```typescript
// src/types/meta.ts
export interface MetaTokens {
access_token: string
token_type: string
expires_in: number
}
export interface FacebookPage {
id: string
name: string
access_token: string // Page-spezifischer Token
category: string
instagram_business_account?: {
id: string
}
}
export interface InstagramAccount {
id: string
username: string
profile_picture_url: string
followers_count: number
media_count: number
}
export interface MetaComment {
id: string
message: string
from: {
id: string
name: string
}
created_time: string
like_count?: number
comment_count?: number
is_hidden?: boolean
}
```
#### 4. API Endpoint
```typescript
// src/app/(payload)/api/auth/meta/route.ts
export async function GET(request: NextRequest) {
// 1. State generieren mit accountType (facebook | instagram)
// 2. Auth URL generieren
// 3. Redirect zu Meta OAuth
}
// src/app/(payload)/api/auth/meta/callback/route.ts
export async function GET(request: NextRequest) {
// 1. Code aus Query extrahieren
// 2. Token austauschen
// 3. Long-lived Token holen (60 Tage gültig)
// 4. Pages/IG Accounts abrufen
// 5. In SocialAccounts speichern
// 6. Redirect zu Admin mit Erfolg
}
```
### Environment Variables
```bash
# .env
META_APP_ID=your_app_id
META_APP_SECRET=your_app_secret
META_REDIRECT_URI=https://your-domain.com/api/auth/meta/callback
```
### Acceptance Criteria
- [ ] OAuth-Flow leitet korrekt zu Meta weiter
- [ ] Callback verarbeitet Token korrekt
- [ ] Long-lived Token wird generiert (60 Tage)
- [ ] Pages und IG-Accounts werden erkannt
- [ ] Credentials werden sicher gespeichert
- [ ] Fehler werden benutzerfreundlich angezeigt
---
## Phase 2.3b: Facebook Integration
**Dauer:** 2-3 Tage
**Abhängigkeiten:** Phase 2.3a abgeschlossen
### Ziel
Facebook Page Kommentare und Nachrichten synchronisieren, analysieren und beantworten.
### Deliverables
#### 1. FacebookClient
```typescript
// src/lib/integrations/meta/FacebookClient.ts
import { MetaBaseClient } from './MetaBaseClient'
export class FacebookClient extends MetaBaseClient {
private pageId: string
private pageAccessToken: string
constructor(pageAccessToken: string, pageId: string)
// Posts
async getPagePosts(limit?: number, since?: Date): Promise<FacebookPost[]>
async getPostComments(postId: string, limit?: number): Promise<MetaComment[]>
// Comments
async replyToComment(commentId: string, message: string): Promise<string>
async hideComment(commentId: string): Promise<void>
async unhideComment(commentId: string): Promise<void>
async deleteComment(commentId: string): Promise<void>
async likeComment(commentId: string): Promise<void>
// Page Messages (Messenger)
async getConversations(limit?: number): Promise<FacebookConversation[]>
async getMessages(conversationId: string): Promise<FacebookMessage[]>
async sendMessage(recipientId: string, message: string): Promise<string>
// Page Info
async getPageInsights(metrics: string[], period: string): Promise<PageInsight[]>
}
```
#### 2. Facebook Types
```typescript
// src/types/facebook.ts
export interface FacebookPost {
id: string
message?: string
story?: string
full_picture?: string
permalink_url: string
created_time: string
shares?: { count: number }
reactions?: { summary: { total_count: number } }
comments?: { summary: { total_count: number } }
}
export interface FacebookConversation {
id: string
participants: {
data: Array<{ id: string; name: string }>
}
updated_time: string
message_count: number
}
export interface FacebookMessage {
id: string
message: string
from: { id: string; name: string }
created_time: string
attachments?: Array<{
type: string
url: string
}>
}
```
#### 3. FacebookContent Collection
```typescript
// src/collections/FacebookContent.ts
{
slug: 'facebook-content',
admin: {
useAsTitle: 'title',
group: 'Social Media',
defaultColumns: ['title', 'socialAccount', 'type', 'postedAt'],
},
fields: [
{ name: 'externalId', type: 'text', required: true, unique: true },
{ name: 'socialAccount', type: 'relationship', relationTo: 'social-accounts', required: true },
{ name: 'type', type: 'select', options: ['post', 'photo', 'video', 'link', 'event'] },
{ name: 'title', type: 'text' }, // Erste Zeile der Message
{ name: 'message', type: 'textarea' },
{ name: 'permalinkUrl', type: 'text' },
{ name: 'imageUrl', type: 'text' },
{ name: 'postedAt', type: 'date' },
{ name: 'stats', type: 'group', fields: [
{ name: 'reactions', type: 'number', defaultValue: 0 },
{ name: 'comments', type: 'number', defaultValue: 0 },
{ name: 'shares', type: 'number', defaultValue: 0 },
{ name: 'reach', type: 'number', defaultValue: 0 },
]},
],
}
```
#### 4. FacebookSyncService
```typescript
// src/lib/integrations/meta/FacebookSyncService.ts
export class FacebookSyncService {
constructor(payload: Payload)
async syncAccount(account: SocialAccount): Promise<SyncResult>
private async syncPosts(client: FacebookClient, account: SocialAccount): Promise<number>
private async syncComments(client: FacebookClient, post: FacebookContent): Promise<number>
private async syncMessages(client: FacebookClient, account: SocialAccount): Promise<number>
private async processComment(comment: MetaComment, account: SocialAccount, content: FacebookContent): Promise<'new' | 'updated' | 'skipped'>
private async processMessage(message: FacebookMessage, account: SocialAccount, conversation: FacebookConversation): Promise<'new' | 'updated' | 'skipped'>
}
```
#### 5. UI Updates
**CommunityInbox.tsx Erweiterungen:**
- Facebook-Icon und Badge
- "Hide Comment" / "Unhide Comment" Actions
- "Like Comment" Action
- Messenger-Konversationen in separatem Tab
- Facebook-spezifische Filter (Posts, Messenger)
**Platform-Icon Mapping:**
```typescript
const platformIcons = {
youtube: <YouTubeIcon />,
facebook: <FacebookIcon />,
instagram: <InstagramIcon />,
tiktok: <TikTokIcon />,
}
```
### Acceptance Criteria
- [ ] Posts werden synchronisiert (letzte 30 Tage)
- [ ] Kommentare werden importiert und analysiert
- [ ] Antworten werden über API gepostet
- [ ] Hide/Unhide funktioniert
- [ ] Messenger-Nachrichten werden synchronisiert
- [ ] Messenger-Antworten funktionieren
- [ ] Analytics zeigt Facebook-Daten
- [ ] Rate Limiting wird beachtet (200 calls/hour)
---
## Phase 2.3c: Instagram Integration
**Dauer:** 2 Tage
**Abhängigkeiten:** Phase 2.3a abgeschlossen (Phase 2.3b parallel möglich)
### Ziel
Instagram-Kommentare und DMs synchronisieren, analysieren und beantworten.
### Deliverables
#### 1. InstagramClient
```typescript
// src/lib/integrations/meta/InstagramClient.ts
import { MetaBaseClient } from './MetaBaseClient'
export class InstagramClient extends MetaBaseClient {
private igAccountId: string
constructor(accessToken: string, igAccountId: string)
// Media
async getMedia(limit?: number, since?: Date): Promise<InstagramMedia[]>
async getMediaComments(mediaId: string): Promise<MetaComment[]>
// Comments
async replyToComment(commentId: string, message: string): Promise<string>
async hideComment(commentId: string): Promise<void>
async deleteComment(commentId: string): Promise<void>
// Mentions & Tags
async getMentions(): Promise<InstagramMention[]>
async getTaggedMedia(): Promise<InstagramMedia[]>
// Stories (Mentions)
async getStoryMentions(): Promise<InstagramStoryMention[]>
// DMs (wenn Scope verfügbar)
async getConversations(): Promise<InstagramConversation[]>
async sendMessage(conversationId: string, message: string): Promise<string>
// Account Info
async getAccountInsights(metrics: string[], period: string): Promise<AccountInsight[]>
}
```
#### 2. Instagram Types
```typescript
// src/types/instagram.ts
export interface InstagramMedia {
id: string
media_type: 'IMAGE' | 'VIDEO' | 'CAROUSEL_ALBUM' | 'REELS'
media_url?: string
thumbnail_url?: string
permalink: string
caption?: string
timestamp: string
like_count: number
comments_count: number
}
export interface InstagramMention {
id: string
caption?: string
media_type: string
permalink: string
timestamp: string
username: string // Who mentioned us
}
export interface InstagramStoryMention {
id: string
media_type: string
media_url: string
timestamp: string
// Story mentions expire after 24h!
}
export interface InstagramConversation {
id: string
participants: Array<{ id: string; username: string }>
updated_time: string
}
```
#### 3. InstagramContent Collection
```typescript
// src/collections/InstagramContent.ts
{
slug: 'instagram-content',
admin: {
useAsTitle: 'caption',
group: 'Social Media',
defaultColumns: ['caption', 'socialAccount', 'mediaType', 'postedAt'],
},
fields: [
{ name: 'externalId', type: 'text', required: true, unique: true },
{ name: 'socialAccount', type: 'relationship', relationTo: 'social-accounts', required: true },
{ name: 'mediaType', type: 'select', options: ['image', 'video', 'carousel', 'reel', 'story'] },
{ name: 'caption', type: 'textarea' },
{ name: 'permalink', type: 'text' },
{ name: 'mediaUrl', type: 'text' },
{ name: 'thumbnailUrl', type: 'text' },
{ name: 'postedAt', type: 'date' },
{ name: 'stats', type: 'group', fields: [
{ name: 'likes', type: 'number', defaultValue: 0 },
{ name: 'comments', type: 'number', defaultValue: 0 },
{ name: 'saves', type: 'number', defaultValue: 0 },
{ name: 'reach', type: 'number', defaultValue: 0 },
{ name: 'impressions', type: 'number', defaultValue: 0 },
]},
],
}
```
#### 4. InstagramSyncService
```typescript
// src/lib/integrations/meta/InstagramSyncService.ts
export class InstagramSyncService {
constructor(payload: Payload)
async syncAccount(account: SocialAccount): Promise<SyncResult>
private async syncMedia(client: InstagramClient, account: SocialAccount): Promise<number>
private async syncComments(client: InstagramClient, media: InstagramContent): Promise<number>
private async syncMentions(client: InstagramClient, account: SocialAccount): Promise<number>
private async syncStoryMentions(client: InstagramClient, account: SocialAccount): Promise<number>
}
```
### Wichtige Hinweise
1. **Story Mentions:** Verfallen nach 24h → Sync alle 6 Stunden oder Webhooks
2. **Token Refresh:** Alle 60 Tage → Automatischen Refresh implementieren
3. **DM Scope:** `instagram_manage_messages` erfordert App-Review
### Acceptance Criteria
- [ ] Media wird synchronisiert (letzte 30 Tage)
- [ ] Kommentare werden importiert und analysiert
- [ ] Antworten werden über API gepostet
- [ ] Mentions werden erkannt und importiert
- [ ] Story-Mentions werden erfasst (falls verfügbar)
- [ ] Hide Comment funktioniert
- [ ] Analytics Dashboard integriert
---
## Phase 2.4: TikTok Integration
**Dauer:** 2-3 Tage
**Abhängigkeiten:** Keine (parallel zu Meta möglich, aber API-Approval erforderlich)
### Ziel
TikTok-Kommentare synchronisieren und beantworten.
### Voraussetzungen
- TikTok Business Account
- TikTok for Developers Zugang
- **App-Genehmigung für Comment API** (WICHTIG: Frühzeitig beantragen!)
### Deliverables
#### 1. TikTok OAuth
```typescript
// src/lib/integrations/tiktok/oauth.ts
const TIKTOK_OAUTH_CONFIG = {
authorizationUrl: 'https://www.tiktok.com/v2/auth/authorize/',
tokenUrl: 'https://open.tiktokapis.com/v2/oauth/token/',
scopes: [
'user.info.basic',
'video.list',
'comment.list',
'comment.list.manage',
],
}
export async function getAuthUrl(state: string): Promise<string>
export async function exchangeCodeForTokens(code: string): Promise<TikTokTokens>
export async function refreshAccessToken(refreshToken: string): Promise<TikTokTokens>
```
#### 2. TikTokClient
```typescript
// src/lib/integrations/tiktok/TikTokClient.ts
export class TikTokClient {
private accessToken: string
private openId: string
constructor(accessToken: string, openId: string)
// Videos
async getVideos(cursor?: number, maxCount?: number): Promise<{
videos: TikTokVideo[]
cursor: number
hasMore: boolean
}>
// Comments
async getVideoComments(videoId: string, cursor?: number): Promise<{
comments: TikTokComment[]
cursor: number
hasMore: boolean
}>
async replyToComment(videoId: string, commentId: string, text: string): Promise<string>
// User Info
async getUserInfo(): Promise<TikTokUser>
}
```
#### 3. TikTok Types
```typescript
// src/types/tiktok.ts
export interface TikTokVideo {
id: string
title: string
description: string
create_time: number // Unix timestamp
cover_image_url: string
share_url: string
duration: number
like_count: number
comment_count: number
share_count: number
view_count: number
}
export interface TikTokComment {
id: string
text: string
create_time: number
like_count: number
reply_count: number
user: {
open_id: string
display_name: string
avatar_url: string
}
parent_comment_id?: string // Für Replies
}
export interface TikTokTokens {
access_token: string
refresh_token: string
expires_in: number
open_id: string
scope: string
}
```
#### 4. TikTokContent Collection
```typescript
// src/collections/TikTokContent.ts
{
slug: 'tiktok-content',
admin: {
useAsTitle: 'title',
group: 'Social Media',
},
fields: [
{ name: 'externalId', type: 'text', required: true, unique: true },
{ name: 'socialAccount', type: 'relationship', relationTo: 'social-accounts', required: true },
{ name: 'title', type: 'text' },
{ name: 'description', type: 'textarea' },
{ name: 'shareUrl', type: 'text' },
{ name: 'coverImageUrl', type: 'text' },
{ name: 'duration', type: 'number' },
{ name: 'postedAt', type: 'date' },
{ name: 'stats', type: 'group', fields: [
{ name: 'views', type: 'number', defaultValue: 0 },
{ name: 'likes', type: 'number', defaultValue: 0 },
{ name: 'comments', type: 'number', defaultValue: 0 },
{ name: 'shares', type: 'number', defaultValue: 0 },
]},
],
}
```
### Wichtige Hinweise
1. **API-Zugang:** Comment API erfordert App-Review (1-4 Wochen)
2. **Sandbox Mode:** Erst in Sandbox testen
3. **Cursor-based Pagination:** Kein Offset-Support
4. **Kein DM-Zugang:** TikTok API erlaubt keine DMs
5. **Rate Limits:** 100 Requests/Minute
### Acceptance Criteria
- [ ] OAuth-Flow funktioniert
- [ ] Videos werden synchronisiert
- [ ] Kommentare werden importiert und analysiert
- [ ] Antworten können gepostet werden
- [ ] Analytics integriert
---
## Phase 2.5a: Auto-Reply Service
**Dauer:** 2-3 Tage
**Abhängigkeiten:** Mindestens eine Social Platform integriert (YouTube reicht)
### Ziel
Backend-Logik für vollautomatische Antworten basierend auf konfigurierbaren Regeln.
### Architektur
```
┌─────────────────────────────────────────────────────────────────┐
│ AUTO-REPLY FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ New Comment ──▶ Claude Analysis ──▶ Safety Check │
│ │ │
│ ┌────────────┴────────────┐ │
│ ▼ ▼ │
│ [SAFE TO AUTO-REPLY] [MANUAL QUEUE] │
│ │ │
│ ▼ │
│ Rule Matching (Priority-sorted) │
│ │ │
│ ┌───────┴───────┐ │
│ ▼ ▼ │
│ [RULE MATCH] [NO MATCH] │
│ │ │ │
│ ▼ ▼ │
│ Generate Reply Manual Queue │
│ │ │
│ ▼ │
│ Rate Limit Check │
│ │ │
│ ┌──────┴──────┐ │
│ ▼ ▼ │
│ [ALLOWED] [LIMITED] │
│ │ │ │
│ ▼ ▼ │
│ Post Reply Queue for Later │
│ │ │
│ ▼ │
│ Log & Update Stats │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Deliverables
#### 1. AutoReplyRules Collection
```typescript
// src/collections/AutoReplyRules.ts
{
slug: 'auto-reply-rules',
admin: {
useAsTitle: 'name',
group: 'Community',
defaultColumns: ['name', 'enabled', 'priority', 'stats.totalFired'],
},
fields: [
// Basis
{ name: 'name', type: 'text', required: true },
{ name: 'description', type: 'textarea' },
{ name: 'enabled', type: 'checkbox', defaultValue: false }, // Default: AUS
{ name: 'priority', type: 'number', defaultValue: 100 }, // Niedriger = höher
// Scope
{
name: 'scope',
type: 'group',
fields: [
{ name: 'platforms', type: 'relationship', relationTo: 'social-platforms', hasMany: true },
{ name: 'accounts', type: 'relationship', relationTo: 'social-accounts', hasMany: true },
{ name: 'channelTypes', type: 'select', hasMany: true, options: ['corporate', 'lifestyle', 'business'] },
]
},
// Conditions (Wann feuert die Regel?)
{
name: 'conditions',
type: 'group',
fields: [
{ name: 'sentiments', type: 'select', hasMany: true, options: [
{ label: 'Positiv', value: 'positive' },
{ label: 'Dankbarkeit', value: 'gratitude' },
{ label: 'Neutral', value: 'neutral' },
{ label: 'Frage', value: 'question' },
]},
{ name: 'minConfidence', type: 'number', defaultValue: 80, min: 0, max: 100 },
{ name: 'keywordsInclude', type: 'array', fields: [{ name: 'keyword', type: 'text' }] },
{ name: 'keywordsExclude', type: 'array', fields: [{ name: 'keyword', type: 'text' }] },
{ name: 'topicsInclude', type: 'array', fields: [{ name: 'topic', type: 'text' }] },
{ name: 'topicsExclude', type: 'array', fields: [{ name: 'topic', type: 'text' }] },
{ name: 'maxMessageLength', type: 'number', defaultValue: 300 },
]
},
// Sicherheits-Ausschlüsse (NIEMALS überschreiben!)
{
name: 'safetyExclusions',
type: 'group',
admin: { description: '⚠️ Diese Ausschlüsse können nicht deaktiviert werden!' },
fields: [
{ name: 'excludeMedicalQuestions', type: 'checkbox', defaultValue: true, admin: { readOnly: true } },
{ name: 'excludeEscalations', type: 'checkbox', defaultValue: true, admin: { readOnly: true } },
{ name: 'excludeNegativeSentiment', type: 'checkbox', defaultValue: true },
{ name: 'excludeFrustration', type: 'checkbox', defaultValue: true },
{ name: 'excludeVerifiedAccounts', type: 'checkbox', defaultValue: true },
{ name: 'excludeInfluencers', type: 'checkbox', defaultValue: true },
{ name: 'influencerThreshold', type: 'number', defaultValue: 10000 },
]
},
// Action
{
name: 'action',
type: 'group',
fields: [
{ name: 'type', type: 'select', required: true, options: [
{ label: 'Template verwenden', value: 'template' },
{ label: 'AI generieren', value: 'ai_generate' },
{ label: 'Template + AI personalisieren', value: 'template_ai' },
]},
{ name: 'template', type: 'relationship', relationTo: 'community-templates',
admin: { condition: (data) => data?.action?.type === 'template' || data?.action?.type === 'template_ai' }
},
{ name: 'aiTone', type: 'select', options: ['freundlich', 'professionell', 'empathisch'],
admin: { condition: (data) => data?.action?.type === 'ai_generate' || data?.action?.type === 'template_ai' }
},
{ name: 'delayMinutes', type: 'number', defaultValue: 2, min: 0, max: 60 },
{ name: 'addSignature', type: 'checkbox', defaultValue: true },
]
},
// Rate Limits
{
name: 'limits',
type: 'group',
fields: [
{ name: 'maxPerHour', type: 'number', defaultValue: 10 },
{ name: 'maxPerDay', type: 'number', defaultValue: 50 },
{ name: 'cooldownPerAuthor', type: 'number', defaultValue: 60, admin: { description: 'Minuten bis gleicher Autor wieder Auto-Reply bekommt' } },
]
},
// Stats
{
name: 'stats',
type: 'group',
admin: { readOnly: true },
fields: [
{ name: 'totalFired', type: 'number', defaultValue: 0 },
{ name: 'successCount', type: 'number', defaultValue: 0 },
{ name: 'failCount', type: 'number', defaultValue: 0 },
{ name: 'lastFiredAt', type: 'date' },
]
},
],
}
```
#### 2. AutoReplyLogs Collection
```typescript
// src/collections/AutoReplyLogs.ts
{
slug: 'auto-reply-logs',
admin: {
group: 'Community',
defaultColumns: ['interaction', 'rule', 'success', 'createdAt'],
},
fields: [
{ name: 'interaction', type: 'relationship', relationTo: 'community-interactions', required: true },
{ name: 'rule', type: 'relationship', relationTo: 'auto-reply-rules', required: true },
{ name: 'decision', type: 'group', fields: [
{ name: 'shouldAutoReply', type: 'checkbox' },
{ name: 'reason', type: 'text' },
{ name: 'blockedBy', type: 'text' }, // z.B. "safety:medical", "limit:hourly"
]},
{ name: 'generatedReply', type: 'textarea' },
{ name: 'postedReply', type: 'textarea' }, // Falls abweichend (z.B. gekürzt)
{ name: 'externalReplyId', type: 'text' },
{ name: 'success', type: 'checkbox' },
{ name: 'error', type: 'text' },
{ name: 'processingTime', type: 'number' }, // ms
{ name: 'postedAt', type: 'date' },
],
}
```
#### 3. AutoReplyService
```typescript
// src/lib/services/AutoReplyService.ts
interface AutoReplyDecision {
shouldAutoReply: boolean
rule?: AutoReplyRule
reason: string
blockedBy?: string
}
interface AutoReplyResult {
success: boolean
replyId?: string
error?: string
processingTime: number
}
export class AutoReplyService {
private payload: Payload
private claudeReply: ClaudeReplyService
constructor(payload: Payload)
/**
* Haupteinsprungspunkt: Prüft und führt Auto-Reply aus
*/
async processInteraction(interaction: CommunityInteraction): Promise<AutoReplyResult | null>
/**
* Schritt 1: Safety-Check (NIEMALS umgehen!)
*/
private async checkSafety(interaction: CommunityInteraction): Promise<{
safe: boolean
reason?: string
}>
/**
* Schritt 2: Finde passende Regel
*/
private async findMatchingRule(interaction: CommunityInteraction): Promise<AutoReplyRule | null>
/**
* Schritt 3: Prüfe Rate Limits
*/
private async checkRateLimits(rule: AutoReplyRule, authorId: string): Promise<{
allowed: boolean
reason?: string
}>
/**
* Schritt 4: Generiere Reply
*/
private async generateReply(interaction: CommunityInteraction, rule: AutoReplyRule): Promise<string>
/**
* Schritt 5: Poste Reply
*/
private async postReply(interaction: CommunityInteraction, replyText: string): Promise<string>
/**
* Schritt 6: Logging
*/
private async logResult(
interaction: CommunityInteraction,
rule: AutoReplyRule,
decision: AutoReplyDecision,
result: AutoReplyResult
): Promise<void>
}
```
#### 4. Safety Rules (KRITISCH - Nicht änderbar!)
```typescript
// src/lib/services/AutoReplyService.ts - Safety Check Implementation
private async checkSafety(interaction: CommunityInteraction): Promise<SafetyResult> {
const flags = interaction.flags
const analysis = interaction.analysis
// ABSOLUTE BLOCKS - NIEMALS Auto-Reply
if (flags?.isMedicalQuestion) {
return { safe: false, reason: 'Medizinische Frage erkannt' }
}
if (flags?.requiresEscalation) {
return { safe: false, reason: 'Eskalation erforderlich' }
}
if (interaction.status !== 'new') {
return { safe: false, reason: 'Bereits bearbeitet' }
}
if (interaction.response?.sentAt) {
return { safe: false, reason: 'Bereits beantwortet' }
}
// CONFIGURABLE BLOCKS (können in Rule überschrieben werden)
// ... sentiment, influencer, verified, etc.
return { safe: true }
}
```
#### 5. Integration in Sync-Flow
```typescript
// Erweitere alle CommentsSyncServices
async processComment(comment: RawComment): Promise<void> {
// 1. In DB speichern
const interaction = await this.saveInteraction(comment)
// 2. Claude-Analyse
const analysis = await this.claudeAnalysis.analyzeComment(interaction.message)
await this.updateAnalysis(interaction.id, analysis)
// 3. Rules Engine (bestehend)
await this.rulesEngine.evaluate(interaction)
// 4. Auto-Reply Check (NEU)
if (this.autoReplyEnabled) {
const autoReplyService = new AutoReplyService(this.payload)
await autoReplyService.processInteraction(interaction)
}
}
```
### Acceptance Criteria
- [ ] Safety-Check blockiert medizinische Fragen IMMER
- [ ] Safety-Check blockiert Eskalationen IMMER
- [ ] Rules werden nach Priorität sortiert ausgewertet
- [ ] Rate Limits funktionieren (per Stunde/Tag/Autor)
- [ ] Logs werden vollständig geschrieben
- [ ] Stats werden aktualisiert
- [ ] Delay vor Antwort funktioniert
---
## Phase 2.5b: Auto-Reply UI & Testing
**Dauer:** 2 Tage
**Abhängigkeiten:** Phase 2.5a abgeschlossen
### Ziel
Admin-UI für Auto-Reply Konfiguration und Testing.
### Deliverables
#### 1. Auto-Reply Manager View
```
src/app/(payload)/admin/views/community/auto-reply/
├── page.tsx
├── AutoReplyManager.tsx
├── components/
│ ├── RulesList.tsx
│ ├── RuleEditor.tsx
│ ├── RuleTester.tsx
│ ├── RuleStats.tsx
│ └── RecentLogs.tsx
└── auto-reply.scss
```
#### 2. UI Features
**Rules List:**
- Alle Regeln mit Enable/Disable Toggle
- Sortiert nach Priorität
- Quick Stats (Fired Count, Success Rate)
- Drag & Drop für Prioritäts-Änderung
**Rule Editor:**
- Vollständiger Regel-Editor
- Condition Builder mit Preview
- Action-Konfiguration
- Limit-Einstellungen
**Rule Tester:**
```typescript
// Test-Input
interface TestInput {
message: string
sentiment?: string
platform?: string
authorFollowers?: number
}
// Test-Output
interface TestOutput {
wouldAutoReply: boolean
matchedRule?: string
reason: string
generatedReply?: string
warnings: string[]
}
```
**Recent Logs:**
- Letzte 100 Auto-Reply Entscheidungen
- Filter: Success/Failed, Rule, Platform
- Details mit Interaction-Link
#### 3. Analytics Integration
**Neue Metriken für Dashboard:**
```typescript
// /api/community/analytics/auto-reply/route.ts
interface AutoReplyStats {
totalProcessed: number
autoReplied: number
autoReplyRate: number
blockedByMedical: number
blockedByEscalation: number
blockedByRateLimit: number
topRules: Array<{
rule: string
fired: number
successRate: number
}>
avgProcessingTime: number
}
```
### Acceptance Criteria
- [ ] Rules können erstellt/bearbeitet werden
- [ ] Enable/Disable funktioniert
- [ ] Tester zeigt korrekte Entscheidungen
- [ ] Logs werden angezeigt
- [ ] Analytics zeigen Auto-Reply Metriken
---
## Phase 3.0a: Scheduled Reports
**Dauer:** 2-3 Tage
**Abhängigkeiten:** E-Mail-Service (existiert bereits)
### Ziel
Automatische Community-Reports per E-Mail.
### Deliverables
#### 1. ReportSchedules Collection
```typescript
// src/collections/ReportSchedules.ts
{
slug: 'report-schedules',
admin: {
useAsTitle: 'name',
group: 'Community',
},
fields: [
{ name: 'name', type: 'text', required: true },
{ name: 'enabled', type: 'checkbox', defaultValue: true },
// Schedule
{ name: 'frequency', type: 'select', required: true, options: [
{ label: 'Täglich', value: 'daily' },
{ label: 'Wöchentlich', value: 'weekly' },
{ label: 'Monatlich', value: 'monthly' },
]},
{ name: 'dayOfWeek', type: 'select', options: [
{ label: 'Montag', value: 'monday' },
// ... andere Tage
], admin: { condition: (data) => data?.frequency === 'weekly' }},
{ name: 'dayOfMonth', type: 'number', min: 1, max: 28,
admin: { condition: (data) => data?.frequency === 'monthly' }},
{ name: 'time', type: 'text', defaultValue: '08:00', admin: { description: 'Format: HH:MM' }},
{ name: 'timezone', type: 'text', defaultValue: 'Europe/Berlin' },
// Recipients
{ name: 'recipients', type: 'array', fields: [
{ name: 'email', type: 'email', required: true },
{ name: 'name', type: 'text' },
]},
// Report Configuration
{ name: 'reportType', type: 'select', required: true, options: [
{ label: 'Übersicht', value: 'overview' },
{ label: 'Sentiment-Analyse', value: 'sentiment' },
{ label: 'Team-Performance', value: 'response_metrics' },
{ label: 'Content-Performance', value: 'content' },
{ label: 'Vollständiger Report', value: 'full' },
]},
{ name: 'channels', type: 'relationship', relationTo: 'social-accounts', hasMany: true },
{ name: 'format', type: 'select', options: ['pdf', 'excel', 'html_email'], defaultValue: 'pdf' },
// Stats
{ name: 'lastSentAt', type: 'date' },
{ name: 'nextScheduledAt', type: 'date' },
{ name: 'sendCount', type: 'number', defaultValue: 0 },
],
}
```
#### 2. ReportGeneratorService
```typescript
// src/lib/services/ReportGeneratorService.ts
export class ReportGeneratorService {
async generateReport(schedule: ReportSchedule): Promise<{
content: Buffer
filename: string
mimeType: string
}>
async sendReport(schedule: ReportSchedule): Promise<void>
private async generatePdfReport(data: ReportData): Promise<Buffer>
private async generateExcelReport(data: ReportData): Promise<Buffer>
private async generateHtmlEmail(data: ReportData): Promise<string>
private async collectReportData(schedule: ReportSchedule): Promise<ReportData>
}
```
#### 3. Cron Job
```typescript
// src/app/(payload)/api/cron/send-reports/route.ts
export async function GET(request: NextRequest) {
// Auth check...
const now = new Date()
const dueReports = await findDueReports(now)
for (const schedule of dueReports) {
try {
const generator = new ReportGeneratorService()
await generator.sendReport(schedule)
// Update nextScheduledAt
await updateNextSchedule(schedule)
} catch (error) {
console.error(`Report ${schedule.id} failed:`, error)
}
}
return Response.json({ success: true, sent: dueReports.length })
}
```
#### 4. vercel.json Update
```json
{
"crons": [
{
"path": "/api/cron/youtube-sync",
"schedule": "*/15 * * * *"
},
{
"path": "/api/cron/send-reports",
"schedule": "0 * * * *"
}
]
}
```
### Acceptance Criteria
- [ ] Reports werden generiert (PDF/Excel/HTML)
- [ ] E-Mail-Versand funktioniert
- [ ] Scheduling funktioniert (daily/weekly/monthly)
- [ ] Timezone-Support
- [ ] Error Handling bei fehlgeschlagenem Versand
---
## Phase 3.0b: Real-time Updates
**Dauer:** 2-3 Tage
**Abhängigkeiten:** Keine
### Ziel
Live-Updates im Dashboard ohne manuellen Refresh.
### Deliverables
#### 1. Server-Sent Events Endpoint
```typescript
// src/app/(payload)/api/community/stream/route.ts
export async function GET(request: NextRequest) {
// Auth check...
const encoder = new TextEncoder()
const stream = new ReadableStream({
async start(controller) {
let lastCheck = new Date()
const sendUpdate = async () => {
try {
const updates = await getUpdatesSince(lastCheck)
lastCheck = new Date()
if (updates.length > 0) {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify(updates)}\n\n`)
)
}
} catch (error) {
console.error('Stream error:', error)
}
}
// Initial send
await sendUpdate()
// Poll every 5 seconds
const interval = setInterval(sendUpdate, 5000)
// Cleanup on close
request.signal.addEventListener('abort', () => {
clearInterval(interval)
controller.close()
})
}
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
}
})
}
```
#### 2. React Hook
```typescript
// src/hooks/useRealtimeUpdates.ts
interface Update {
type: 'new_interaction' | 'status_change' | 'new_reply'
data: any
timestamp: string
}
export function useRealtimeUpdates() {
const [updates, setUpdates] = useState<Update[]>([])
const [connected, setConnected] = useState(false)
useEffect(() => {
const eventSource = new EventSource('/api/community/stream')
eventSource.onopen = () => setConnected(true)
eventSource.onerror = () => setConnected(false)
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data)
setUpdates(prev => [...data, ...prev].slice(0, 100))
}
return () => {
eventSource.close()
setConnected(false)
}
}, [])
return { updates, connected }
}
```
#### 3. UI Integration
**Dashboard Updates:**
- Live-Counter Badge bei neuen Items
- Toast-Notifications bei wichtigen Events
- Auto-Refresh der Liste bei neuen Items
- Pulsierender Indicator wenn verbunden
### Acceptance Criteria
- [ ] SSE-Verbindung funktioniert
- [ ] Updates werden in Echtzeit angezeigt
- [ ] Toast-Notifications bei neuen Kommentaren
- [ ] Reconnect bei Verbindungsabbruch
- [ ] Kein Performance-Impact bei vielen Clients
---
## Zusammenfassung der Phasen
| Phase | Name | Dauer | Abhängigkeiten | Priorität |
|-------|------|-------|----------------|-----------|
| 2.3a | Meta OAuth | 1-2 Tage | Meta App Setup | Hoch |
| 2.3b | Facebook Integration | 2-3 Tage | 2.3a | Hoch |
| 2.3c | Instagram Integration | 2 Tage | 2.3a | Hoch |
| 2.4 | TikTok Integration | 2-3 Tage | API Approval | Mittel |
| 2.5a | Auto-Reply Service | 2-3 Tage | Keine | Mittel |
| 2.5b | Auto-Reply UI | 2 Tage | 2.5a | Mittel |
| 3.0a | Scheduled Reports | 2-3 Tage | Keine | Niedrig |
| 3.0b | Real-time Updates | 2-3 Tage | Keine | Niedrig |
**Gesamtdauer:** ~16-21 Tage
---
## Empfohlene Reihenfolge
```
Woche 1: 2.3a (Meta OAuth) → 2.3b (Facebook) + 2.3c (Instagram parallel)
Woche 2: 2.5a (Auto-Reply Service) → 2.5b (Auto-Reply UI)
Woche 3: 2.4 (TikTok, falls API approved) + 3.0a (Reports)
Woche 4: 3.0b (Real-time) + Testing & Polish
```
---
*Erstellt: 16. Januar 2026*
*Basierend auf: Community Management Roadmap Prompt (Konzept-KI)*
*Erweitert um: Facebook-Integration, Logische Aufteilung*
*Für: Dev-KI (Claude Code)*