mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 22:04:10 +00:00
- 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>
1382 lines
42 KiB
Markdown
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)*
|