Files
igny8/v2/V2-Execution-Docs/02H-socializer.md
IGNY8 VPS (Salman) 0570052fec 1
2026-03-23 17:20:51 +00:00

783 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IGNY8 Phase 2: Socializer (02H)
## Native Social Media Posting & Engagement Engine — Stage 8
**Document Version:** 1.0
**Date:** 2026-03-23
**Phase:** IGNY8 Phase 2 — Feature Expansion
**Status:** Build Ready
**Source of Truth:** Codebase at `/data/app/igny8/`
**Audience:** Claude Code, Backend Developers, Architects
---
## 1. CURRENT STATE
### Social Media Today
There is **no** social media functionality in IGNY8. No OAuth connections to any social platform exist. No social post generation, scheduling, or analytics. The automation pipeline ends at Stage 7 (publish to WordPress).
### What Exists
- 7-stage automation pipeline (01E) terminates at publish — no post-publish social distribution
- `SiteIntegration` model (integration app) — handles WordPress OAuth; pattern reusable for social OAuth
- `Content` model — stores the content that feeds social post generation
- `Images` model (writer app) — stores generated images that can be resized for social platforms
- `AutomationConfig` model (automation app) — per-site automation settings; extendable for social toggles
- Celery infrastructure with beat scheduler — ready for social scheduling tasks
### What Does Not Exist
- No social app or models
- No OAuth connections for LinkedIn, Twitter/X, Facebook, Instagram, TikTok
- No AI social post generation or platform adaptation
- No scheduling/calendar system
- No engagement tracking
- No Stage 8 pipeline integration
---
## 2. WHAT TO BUILD
### Overview
Build Stage 8 of the automation pipeline: a social media engine that connects to 5 platforms via OAuth, generates AI-adapted posts from published content, schedules posts with best-time optimization, and tracks engagement metrics.
### 2.1 Platform Support
| Platform | Max Length | Key Format | OAuth Flow | API |
|----------|-----------|------------|------------|-----|
| **LinkedIn** | 1,300 chars | Professional, industry insights | OAuth 2.0 (3-legged) | Marketing API v2 |
| **Twitter/X** | 280 chars (thread option) | Concise, hashtags, hooks | OAuth 2.0 (PKCE) | API v2 |
| **Facebook** | ~63,206 chars (500 optimal) | Conversational, visual | OAuth 2.0 (Facebook Login) | Graph API v18+ |
| **Instagram** | 2,200 chars, 30 hashtags | Visual-first, hashtag-heavy | Via Facebook Business | Graph API (IG) |
| **TikTok** | varies | Gen-Z tone, trending hooks | OAuth 2.0 | Content Posting API |
### 2.2 OAuth Infrastructure
Reusable OAuth service structure:
**Location:** `igny8_core/integration/oauth/`
- `base_oauth.py` — base class with token encrypt/decrypt, refresh logic
- `linkedin_oauth.py` — LinkedIn 3-legged OAuth, scopes: `w_member_social`, `r_organization_social`, `w_organization_social`
- `twitter_oauth.py` — Twitter OAuth 2.0 with PKCE
- `facebook_oauth.py` — Facebook Login, page selection, scopes: `pages_manage_posts`, `pages_read_engagement`
- `instagram_oauth.py` — via Facebook Business, Instagram Basic Display API
- `tiktok_oauth.py` — TikTok OAuth 2.0
**Token Security:**
- Access tokens and refresh tokens encrypted using the same encryption pattern as GSC tokens (02C)
- Tokens stored in `SocialAccount.access_token` and `SocialAccount.refresh_token` (encrypted TextField)
- Auto-refresh via Celery task (`refresh_social_tokens`) runs hourly
**Platform-Specific Auth Notes:**
- **Facebook:** Requires App Review for `pages_manage_posts` permission. Flow: Login → Page Selection → Permission Grant → Store `page_access_token`. Webhook subscription for engagement updates.
- **LinkedIn:** Requires Marketing API Partner Program access. Flow: Authorization → Organization Selection → Store `access_token`.
- **Instagram:** No direct OAuth — connected via Facebook Business account. Requires Facebook Page linked to Instagram Professional account.
- **Multi-account support:** Multiple SocialAccount records per platform per site.
### 2.3 AI Content Adaptation
**Input:** Content record (title, content_html, meta_description, cluster keywords from SAGCluster)
**Per platform, AI generates:**
- Adapted text matching platform tone + length limits
- Hashtag set (topic-aware, platform-appropriate count)
- CTA appropriate to platform conventions
- Image sizing recommendations
**Platform-Specific Generation:**
- **LinkedIn:** Professional tone, industry framing, 3-5 hashtags, link in text
- **Twitter/X:** Hook + key point + CTA in 280 chars. Thread generation option (2-10 tweets) for long content
- **Facebook:** Conversational, 2-3 paragraphs, 3-5 hashtags, link at end
- **Instagram:** Visual-first caption, 20-30 hashtags in first comment or caption, no clickable link (use "link in bio" CTA)
- **TikTok:** Gen-Z/casual tone, trending hook format, 3-5 hashtags
### 2.4 Post Types
| # | Post Type | Description | Platforms |
|---|-----------|-------------|-----------|
| 1 | **Announcement** | Link post with title + excerpt + URL | All |
| 2 | **Highlights** | Key takeaways from article + CTA | All |
| 3 | **Quote Card** | Branded insight/statistic as image post | All |
| 4 | **FAQ Snippet** | Single FAQ from content as post | LinkedIn, Twitter, Facebook |
| 5 | **Carousel** | Multi-image posts (future) | Instagram, LinkedIn |
### 2.5 Image Sizing per Platform
| Platform | Dimension | Aspect Ratio |
|----------|-----------|-------------|
| LinkedIn | 1200×627px | ~1.91:1 |
| Twitter | 1600×900px | ~16:9 |
| Facebook | 1200×630px | ~1.91:1 |
| Instagram (square) | 1080×1080px | 1:1 |
| Instagram (portrait) | 1080×1350px | 4:5 |
| TikTok | 1080×1920px | 9:16 |
Image resizing uses the `Images` model records generated by pipeline Stages 5-6, resized to platform specs using Pillow.
### 2.6 Scheduling & Calendar
**Best-Time Algorithm:**
- Configurable default best-time slots per platform
- Per-account override via `SocialAccount.settings.best_times[]`
- If engagement data available, learn optimal posting times from past performance
**Queue System:**
- Posts with `status='scheduled'` and `scheduled_at` in the past → Celery task publishes
- Celery task `publish_scheduled_posts` runs every minute
- Frequency caps: max posts per day per platform (configurable, default: 2)
- Cooldown: 4-6 hours between posts to same platform (configurable via `SocialAccount.settings.cooldown_hours`)
**Calendar View:**
- API endpoint returns week/month layout of scheduled + published posts
- Frontend renders visual calendar with drag-drop rescheduling (`.tsx` component)
**Bulk Scheduling:**
- Select multiple content → generate social posts for all connected platforms
- Auto-space across optimal times over days/weeks
- Review before confirming schedule
### 2.7 Engagement Tracking
- Celery task `fetch_engagement` runs every 6 hours
- Fetches metrics from each platform API for recent posts (last 30 days)
- Metrics: likes, comments, shares/retweets, impressions, clicks, engagement_rate
- Aggregate dashboard: per-platform performance, best performing posts, optimal posting times
- Attribution: UTM parameters appended to all shared URLs (`utm_source={platform}&utm_medium=social&utm_campaign=igny8`)
### 2.8 Stage 8 Pipeline Integration
After Stage 7 (publish to WordPress), if social accounts are connected for the site:
1. Pipeline triggers Stage 8
2. Load all connected `SocialAccount` records for the site
3. For each connected account, AI generates platform-adapted post
4. **If `AutomationConfig.auto_social_publish` enabled:** posts queue immediately at next best-time slot
5. **If manual review enabled:** posts go to `status='draft'` for user review
6. Published posts logged with `platform_post_id` for engagement tracking
**AutomationConfig Extension:**
```python
# Add to AutomationConfig.settings JSONField:
{
"social_enabled": True, # Master toggle
"auto_social_publish": False, # Auto-queue or manual review
"social_platforms": ["linkedin", "twitter"], # Which platforms to auto-post
"social_post_types": ["announcement", "highlights"], # Which post types
}
```
---
## 3. DATA MODELS & APIS
### 3.1 New Models
All models in a new `social` app.
#### SocialAccount (social app)
```python
class SocialAccount(SiteSectorBaseModel):
"""
Connected social media account for a site.
Stores OAuth tokens (encrypted) and per-account settings.
Multi-account support: multiple accounts per platform per site.
"""
platform = models.CharField(
max_length=15,
choices=[
('linkedin', 'LinkedIn'),
('twitter', 'Twitter/X'),
('facebook', 'Facebook'),
('instagram', 'Instagram'),
('tiktok', 'TikTok'),
]
)
platform_account_id = models.CharField(
max_length=255,
help_text='External platform account/page ID'
)
account_name = models.CharField(max_length=255, help_text='Display name')
account_type = models.CharField(
max_length=20,
choices=[
('personal', 'Personal'),
('page', 'Page'),
('organization', 'Organization'),
],
default='personal'
)
access_token = models.TextField(
help_text='Encrypted OAuth access token'
)
refresh_token = models.TextField(
blank=True,
default='',
help_text='Encrypted OAuth refresh token (nullable for platforms without refresh)'
)
token_expiry = models.DateTimeField(null=True, blank=True)
permissions = models.JSONField(
default=list,
help_text='Granted OAuth scopes'
)
status = models.CharField(
max_length=10,
choices=[
('active', 'Active'),
('expired', 'Expired'),
('revoked', 'Revoked'),
('error', 'Error'),
],
default='active'
)
settings = models.JSONField(
default=dict,
help_text='{auto_publish, max_per_day, best_times[], cooldown_hours}'
)
class Meta:
app_label = 'social'
db_table = 'igny8_social_accounts'
```
**PK:** BigAutoField (integer) — inherits from SiteSectorBaseModel
#### SocialPost (social app)
```python
class SocialPost(SiteSectorBaseModel):
"""
A social media post — generated from content or standalone.
Tracks full lifecycle from draft to published with engagement.
"""
content = models.ForeignKey(
'writer.Content',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='social_posts',
help_text='Source content (null for standalone posts)'
)
social_account = models.ForeignKey(
'social.SocialAccount',
on_delete=models.CASCADE,
related_name='posts'
)
platform = models.CharField(
max_length=15,
help_text='Denormalized from social_account for filtering'
)
post_type = models.CharField(
max_length=15,
choices=[
('announcement', 'Announcement'),
('highlights', 'Highlights'),
('quote_card', 'Quote Card'),
('faq_snippet', 'FAQ Snippet'),
('carousel', 'Carousel'),
]
)
text = models.TextField()
hashtags = models.JSONField(default=list, help_text='List of hashtag strings')
media_urls = models.JSONField(default=list, help_text='List of image/video URLs')
link_url = models.URLField(blank=True, default='')
utm_params = models.JSONField(
default=dict,
blank=True,
help_text='{utm_source, utm_medium, utm_campaign}'
)
thread_posts = models.JSONField(
null=True,
blank=True,
help_text='For Twitter threads: [{text, media_url}]'
)
scheduled_at = models.DateTimeField(null=True, blank=True)
published_at = models.DateTimeField(null=True, blank=True)
platform_post_id = models.CharField(
max_length=255,
blank=True,
default='',
help_text='External post ID after publishing'
)
platform_post_url = models.URLField(blank=True, default='')
status = models.CharField(
max_length=15,
choices=[
('draft', 'Draft'),
('scheduled', 'Scheduled'),
('publishing', 'Publishing'),
('published', 'Published'),
('failed', 'Failed'),
('cancelled', 'Cancelled'),
],
default='draft'
)
error_message = models.TextField(blank=True, default='')
class Meta:
app_label = 'social'
db_table = 'igny8_social_posts'
```
**PK:** BigAutoField (integer) — inherits from SiteSectorBaseModel
#### SocialEngagement (social app)
```python
class SocialEngagement(models.Model):
"""
Engagement metrics for a published social post.
Fetched periodically from platform APIs.
"""
social_post = models.ForeignKey(
'social.SocialPost',
on_delete=models.CASCADE,
related_name='engagement_records'
)
likes = models.IntegerField(default=0)
comments = models.IntegerField(default=0)
shares = models.IntegerField(default=0)
impressions = models.IntegerField(default=0)
clicks = models.IntegerField(default=0)
engagement_rate = models.FloatField(default=0.0)
raw_data = models.JSONField(default=dict, help_text='Full platform API response')
fetched_at = models.DateTimeField(auto_now_add=True)
class Meta:
app_label = 'social'
db_table = 'igny8_social_engagement'
```
**PK:** BigAutoField (integer) — standard Django Model (not AccountBaseModel since engagement is tied to post)
### 3.2 New App Registration
Create social app:
- **App config:** `igny8_core/modules/social/apps.py` with `app_label = 'social'`
- **Add to INSTALLED_APPS** in `igny8_core/settings.py`
### 3.3 Migration
```
igny8_core/migrations/XXXX_add_social_models.py
```
**Operations:**
1. `CreateModel('SocialAccount', ...)` — with index on platform, status
2. `CreateModel('SocialPost', ...)` — with indexes on social_account, platform, status, scheduled_at
3. `CreateModel('SocialEngagement', ...)` — with index on social_post
### 3.4 API Endpoints
All endpoints under `/api/v1/social/`:
#### Account Management
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/social/accounts/connect/{platform}/` | Initiate OAuth flow for platform. Returns redirect URL. |
| GET | `/api/v1/social/accounts/callback/{platform}/` | OAuth callback handler. Exchanges code for tokens, creates SocialAccount. |
| GET | `/api/v1/social/accounts/?site_id=X` | List connected accounts for site. |
| DELETE | `/api/v1/social/accounts/{id}/` | Disconnect account (revoke tokens, soft-delete). |
| PUT | `/api/v1/social/accounts/{id}/settings/` | Update account settings (max_per_day, best_times, cooldown). |
#### Post Management
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/social/posts/generate/` | AI-generate posts for content across all connected platforms. Body: `{content_id, post_types: []}`. |
| POST | `/api/v1/social/posts/` | Create manual post. Body: `{social_account_id, text, ...}`. |
| GET | `/api/v1/social/posts/?site_id=X` | List posts with filters (platform, status, date range). |
| PUT | `/api/v1/social/posts/{id}/` | Edit draft/scheduled post. |
| POST | `/api/v1/social/posts/{id}/publish/` | Publish immediately. |
| POST | `/api/v1/social/posts/{id}/schedule/` | Schedule for later. Body: `{scheduled_at}`. |
| DELETE | `/api/v1/social/posts/{id}/` | Cancel/delete post. |
| POST | `/api/v1/social/posts/bulk-generate/` | Generate for multiple content. Body: `{content_ids: [int]}`. |
| POST | `/api/v1/social/posts/bulk-schedule/` | Schedule multiple posts. Body: `{post_ids: [int], auto_space: true}`. |
#### Calendar & Analytics
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/social/calendar/?site_id=X&month=YYYY-MM` | Calendar view — scheduled + published posts for month. |
| GET | `/api/v1/social/analytics/?site_id=X` | Aggregate analytics: per-platform performance, top posts. |
| GET | `/api/v1/social/analytics/posts/?site_id=X` | Per-post analytics with engagement breakdown. |
**Permissions:** All endpoints use `SiteSectorModelViewSet` permission patterns.
### 3.5 AI Function — GenerateSocialPostsFunction
**Registry key:** `generate_social_posts`
**Location:** `igny8_core/ai/functions/generate_social_posts.py`
```python
class GenerateSocialPostsFunction(BaseAIFunction):
"""
Generates platform-adapted social media posts from content.
One call produces posts for all requested platforms.
"""
function_name = 'generate_social_posts'
def validate(self, content_id, platforms=None, post_types=None, **kwargs):
# Verify content exists, has content_html
# Verify requested platforms have connected accounts
pass
def prepare(self, content_id, platforms=None, post_types=None, **kwargs):
# Load Content record
# Load cluster keywords for hashtag generation
# Load connected SocialAccount records for the site
# Determine applicable post_types per platform
pass
def build_prompt(self):
# Per platform, build adaptation instructions:
# - Platform tone + length limits
# - Content title, meta_description, key points from content_html
# - Hashtag generation instructions
# - CTA format
# - Thread instructions (Twitter if content > 280 chars summary)
pass
def parse_response(self, response):
# Parse per-platform posts:
# {platform: {text, hashtags[], cta, thread_posts?}}
pass
def save_output(self, parsed):
# Create SocialPost records per platform per post_type
# Attach UTM parameters
# Set status='draft' (default) or 'scheduled' if auto-publish
pass
```
### 3.6 OAuth Service
**Location:** `igny8_core/integration/oauth/`
```python
# base_oauth.py
class BaseOAuthService:
"""Base class for OAuth integrations. Handles token encryption/decryption."""
def encrypt_token(self, token):
"""Encrypt token using Django's Fernet key (same pattern as 02C GSC tokens)."""
pass
def decrypt_token(self, encrypted_token):
"""Decrypt stored token."""
pass
def get_authorization_url(self, site_id, account_id=None):
"""Generate OAuth authorization URL with state parameter."""
pass
def handle_callback(self, code, state):
"""Exchange authorization code for tokens, create/update SocialAccount."""
pass
def refresh_tokens(self, social_account):
"""Refresh expired access token using refresh_token."""
pass
# linkedin_oauth.py
class LinkedInOAuthService(BaseOAuthService):
"""LinkedIn Marketing API OAuth 2.0 (3-legged)."""
SCOPES = ['w_member_social', 'r_organization_social', 'w_organization_social']
# ...
# twitter_oauth.py
class TwitterOAuthService(BaseOAuthService):
"""Twitter/X OAuth 2.0 with PKCE."""
SCOPES = ['tweet.read', 'tweet.write', 'users.read']
# ...
# facebook_oauth.py
class FacebookOAuthService(BaseOAuthService):
"""Facebook Login with page selection."""
SCOPES = ['pages_manage_posts', 'pages_read_engagement']
# ...
# tiktok_oauth.py
class TikTokOAuthService(BaseOAuthService):
"""TikTok Content Posting API OAuth."""
# ...
```
### 3.7 Social Publisher Service
**Location:** `igny8_core/business/social_publisher.py`
```python
class SocialPublisherService:
"""
Handles actual posting to platform APIs.
Routes to platform-specific publishers.
"""
PUBLISHERS = {
'linkedin': LinkedInPublisher,
'twitter': TwitterPublisher,
'facebook': FacebookPublisher,
'instagram': InstagramPublisher,
'tiktok': TikTokPublisher,
}
def publish(self, social_post_id):
"""
Publish a SocialPost to its target platform.
1. Load SocialPost + SocialAccount
2. Decrypt access token
3. Route to platform-specific publisher
4. Update SocialPost with platform_post_id, platform_post_url, published_at
5. Set status='published' or 'failed'
"""
pass
```
### 3.8 Celery Tasks
**Location:** `igny8_core/tasks/social_tasks.py`
```python
@shared_task(name='publish_scheduled_posts')
def publish_scheduled_posts():
"""
Runs every minute. Finds SocialPost records with
status='scheduled' and scheduled_at <= now(). Publishes each.
"""
pass
@shared_task(name='refresh_social_tokens')
def refresh_social_tokens():
"""
Runs hourly. Refreshes tokens expiring within next 2 hours.
Updates SocialAccount.access_token, token_expiry.
Sets status='expired' if refresh fails.
"""
pass
@shared_task(name='fetch_engagement')
def fetch_engagement():
"""
Runs every 6 hours. Fetches engagement metrics from platform APIs
for all published posts in last 30 days.
Creates SocialEngagement records.
"""
pass
@shared_task(name='generate_social_posts_stage8')
def generate_social_posts_stage8(content_id):
"""
Triggered by pipeline after Stage 7 (publish).
Generates social posts for all connected platforms for the site.
"""
pass
```
**Beat Schedule Additions:**
| Task | Schedule | Notes |
|------|----------|-------|
| `publish_scheduled_posts` | Every minute | Publishes due scheduled posts |
| `refresh_social_tokens` | Hourly | Refreshes expiring OAuth tokens |
| `fetch_engagement` | Every 6 hours | Fetches engagement metrics for recent posts |
### 3.9 Image Resizing Service
**Location:** `igny8_core/business/social_image_resize.py`
```python
class SocialImageResizeService:
"""
Resizes images from the Images model to platform-specific dimensions.
Uses Pillow for image processing.
"""
PLATFORM_SIZES = {
'linkedin': (1200, 627),
'twitter': (1600, 900),
'facebook': (1200, 630),
'instagram_square': (1080, 1080),
'instagram_portrait': (1080, 1350),
'tiktok': (1080, 1920),
}
def resize(self, image_path, platform, variant='default'):
"""Resize image, return new file path."""
pass
```
---
## 4. IMPLEMENTATION STEPS
### Step 1: Create Social App
1. Create `igny8_core/modules/social/` directory with `__init__.py` and `apps.py`
2. Add `social` to `INSTALLED_APPS` in settings.py
3. Create models: SocialAccount, SocialPost, SocialEngagement
### Step 2: Migration
1. Create migration for 3 new models
2. Run migration
### Step 3: OAuth Infrastructure
1. Create `igny8_core/integration/oauth/` directory
2. Implement `BaseOAuthService` with token encryption/decryption
3. Implement platform-specific OAuth handlers (LinkedIn, Twitter, Facebook, TikTok)
4. Instagram OAuth routes through Facebook Business
### Step 4: AI Function
1. Implement `GenerateSocialPostsFunction` in `igny8_core/ai/functions/generate_social_posts.py`
2. Register `generate_social_posts` in `igny8_core/ai/registry.py`
### Step 5: Services
1. Implement `SocialPublisherService` in `igny8_core/business/social_publisher.py`
2. Implement platform-specific publishers (LinkedIn, Twitter, Facebook, Instagram, TikTok)
3. Implement `SocialImageResizeService` in `igny8_core/business/social_image_resize.py`
### Step 6: Pipeline Integration
Add Stage 8 trigger after Stage 7 (publish):
```python
# In pipeline after publish completes:
def post_publish(content_id):
site = content.site
# Check if social is enabled for this site
config = AutomationConfig.objects.get(site=site)
if config.settings.get('social_enabled'):
generate_social_posts_stage8.delay(content_id)
```
### Step 7: API Endpoints
1. Create `igny8_core/urls/social.py` with account, post, calendar, and analytics endpoints
2. Create views: `AccountConnectView`, `AccountCallbackView`, `AccountListView`
3. Create `PostGenerateView`, `PostViewSet`, `CalendarView`, `AnalyticsView`
4. Register URL patterns under `/api/v1/social/`
### Step 8: Celery Tasks
1. Implement 4 tasks in `igny8_core/tasks/social_tasks.py`
2. Add `publish_scheduled_posts`, `refresh_social_tokens`, `fetch_engagement` to beat schedule
### Step 9: Serializers & Admin
1. Create DRF serializers for SocialAccount (exclude encrypted tokens from response), SocialPost, SocialEngagement
2. Register models in Django admin
### Step 10: Credit Cost Configuration
Add to `CreditCostConfig` (billing app):
| operation_type | default_cost | description |
|---------------|-------------|-------------|
| `social_adaptation` | 1 | AI-generate post for one platform |
| `social_hashtag` | 0.5 | Hashtag generation for one post |
| `social_thread` | 2 | Twitter thread generation (2-10 tweets) |
| `social_carousel` | 5 | Carousel post generation |
| `social_image_resize` | 3-10 | Image resizing/generation per platform |
| `social_suite` | 15-25 | Full suite for all 5 platforms |
---
## 5. ACCEPTANCE CRITERIA
### OAuth Connections
- [ ] LinkedIn OAuth 2.0 connects and stores encrypted tokens
- [ ] Twitter/X OAuth 2.0 with PKCE connects successfully
- [ ] Facebook Login with page selection works, stores page_access_token
- [ ] Instagram connects via Facebook Business account
- [ ] TikTok OAuth connects and stores tokens
- [ ] Token refresh runs hourly, updates expired tokens
- [ ] Multi-account support: multiple accounts per platform per site
### Post Generation
- [ ] AI generates platform-adapted text for each connected platform
- [ ] LinkedIn posts use professional tone, 1,300 char limit
- [ ] Twitter posts respect 280 char limit, thread option for long content
- [ ] Facebook posts use conversational tone, optimal 500 char length
- [ ] Instagram captions include 20-30 hashtags, "link in bio" CTA
- [ ] TikTok posts use casual/Gen-Z tone, trending hook format
- [ ] UTM parameters appended to all shared URLs
### Post Types
- [ ] Announcement, Highlights, Quote Card, FAQ Snippet post types generated
- [ ] Post type selection respects platform compatibility
### Scheduling
- [ ] Posts can be scheduled for future time
- [ ] `publish_scheduled_posts` task runs every minute, publishes due posts
- [ ] Frequency caps enforced (max per day per platform)
- [ ] Cooldown period enforced between posts to same platform
- [ ] Calendar endpoint returns month view of scheduled/published posts
- [ ] Bulk scheduling auto-spaces posts across optimal times
### Engagement
- [ ] Engagement metrics fetched every 6 hours for recent posts
- [ ] SocialEngagement records created with likes, comments, shares, impressions, clicks
- [ ] Aggregate analytics endpoint returns per-platform performance + top posts
- [ ] Per-post analytics available with engagement breakdown
### Pipeline Integration
- [ ] Stage 8 triggers automatically after Stage 7 (publish)
- [ ] Social posts generated for all connected platforms
- [ ] auto_social_publish toggle controls immediate vs manual review
- [ ] Platform selection configurable via AutomationConfig.settings
### Images
- [ ] Images from pipeline resized to platform-specific dimensions
- [ ] media_urls populated on SocialPost records
---
## 6. CLAUDE CODE INSTRUCTIONS
### File Locations
```
igny8_core/
├── modules/
│ └── social/
│ ├── __init__.py
│ ├── apps.py # app_label = 'social'
│ └── models.py # SocialAccount, SocialPost, SocialEngagement
├── ai/
│ └── functions/
│ └── generate_social_posts.py # GenerateSocialPostsFunction
├── integration/
│ └── oauth/
│ ├── __init__.py
│ ├── base_oauth.py # BaseOAuthService
│ ├── linkedin_oauth.py
│ ├── twitter_oauth.py
│ ├── facebook_oauth.py
│ ├── instagram_oauth.py # Routes through Facebook
│ └── tiktok_oauth.py
├── business/
│ ├── social_publisher.py # SocialPublisherService + platform publishers
│ └── social_image_resize.py # SocialImageResizeService
├── tasks/
│ └── social_tasks.py # Celery tasks
├── urls/
│ └── social.py # Social endpoints
└── migrations/
└── XXXX_add_social_models.py
```
### Conventions
- **PKs:** BigAutoField (integer) — do NOT use UUIDs
- **Table prefix:** `igny8_` on all new tables
- **App label:** `social` (new app)
- **Celery app name:** `igny8_core`
- **URL pattern:** `/api/v1/social/...`
- **Permissions:** Use `SiteSectorModelViewSet` permission pattern
- **Token encryption:** Same Fernet pattern as 02C GSC tokens — NEVER expose raw tokens in API responses
- **AI functions:** Extend `BaseAIFunction`; register as `generate_social_posts`
- **Frontend:** `.tsx` files with Zustand stores
### Cross-References
| Doc | Relationship |
|-----|-------------|
| **01E** | Pipeline Stage 8 integration — hooks after Stage 7 publish |
| **02I** | Video creator shares social posting for video content; reuses SocialAccount OAuth for YouTube/TikTok |
| **02C** | GSC token encryption pattern reused for social OAuth tokens |
| **03A** | WP plugin standalone has share buttons — different from this (posting FROM IGNY8) |
| **04A** | Managed services include social media management as a service tier |
### Key Decisions
1. **New `social` app** — Separate from integration because social media is a distinct domain with its own models and business logic
2. **SocialEngagement extends models.Model, not SiteSectorBaseModel** — Engagement records are tied to posts, not directly to sites/sectors
3. **Denormalized `platform` on SocialPost** — Avoids join to SocialAccount for platform-based queries and filtering
4. **OAuth tokens encrypted at rest** — Same Fernet encryption as 02C; tokens never returned in API responses
5. **AutomationConfig.settings extension** — Social pipeline toggles added to existing settings JSONField rather than new model fields