Base code
This commit is contained in:
29
backend/api/facebook_writer/services/__init__.py
Normal file
29
backend/api/facebook_writer/services/__init__.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""Facebook Writer Services."""
|
||||
|
||||
from .base_service import FacebookWriterBaseService
|
||||
from .post_service import FacebookPostService
|
||||
from .story_service import FacebookStoryService
|
||||
from .ad_copy_service import FacebookAdCopyService
|
||||
from .remaining_services import (
|
||||
FacebookReelService,
|
||||
FacebookCarouselService,
|
||||
FacebookEventService,
|
||||
FacebookHashtagService,
|
||||
FacebookEngagementService,
|
||||
FacebookGroupPostService,
|
||||
FacebookPageAboutService
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"FacebookWriterBaseService",
|
||||
"FacebookPostService",
|
||||
"FacebookStoryService",
|
||||
"FacebookReelService",
|
||||
"FacebookCarouselService",
|
||||
"FacebookEventService",
|
||||
"FacebookHashtagService",
|
||||
"FacebookEngagementService",
|
||||
"FacebookGroupPostService",
|
||||
"FacebookPageAboutService",
|
||||
"FacebookAdCopyService"
|
||||
]
|
||||
350
backend/api/facebook_writer/services/ad_copy_service.py
Normal file
350
backend/api/facebook_writer/services/ad_copy_service.py
Normal file
@@ -0,0 +1,350 @@
|
||||
"""Facebook Ad Copy generation service."""
|
||||
|
||||
from typing import Dict, Any, List
|
||||
from ..models.ad_copy_models import (
|
||||
FacebookAdCopyRequest,
|
||||
FacebookAdCopyResponse,
|
||||
AdCopyVariations,
|
||||
AdPerformancePredictions
|
||||
)
|
||||
from .base_service import FacebookWriterBaseService
|
||||
|
||||
|
||||
class FacebookAdCopyService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook ad copy."""
|
||||
|
||||
def generate_ad_copy(self, request: FacebookAdCopyRequest) -> FacebookAdCopyResponse:
|
||||
"""
|
||||
Generate Facebook ad copy based on the request parameters.
|
||||
|
||||
Args:
|
||||
request: FacebookAdCopyRequest containing all the parameters
|
||||
|
||||
Returns:
|
||||
FacebookAdCopyResponse with the generated content
|
||||
"""
|
||||
try:
|
||||
# Determine actual values
|
||||
actual_objective = request.custom_objective if request.ad_objective.value == "Custom" else request.ad_objective.value
|
||||
actual_budget = request.custom_budget if request.budget_range.value == "Custom" else request.budget_range.value
|
||||
actual_age = request.targeting_options.custom_age if request.targeting_options.age_group.value == "Custom" else request.targeting_options.age_group.value
|
||||
|
||||
# Generate primary ad copy
|
||||
primary_copy = self._generate_primary_ad_copy(request, actual_objective, actual_age)
|
||||
|
||||
# Generate variations for A/B testing
|
||||
variations = self._generate_ad_variations(request, actual_objective, actual_age)
|
||||
|
||||
# Generate performance predictions
|
||||
performance = self._generate_performance_predictions(request, actual_budget)
|
||||
|
||||
# Generate suggestions and tips
|
||||
targeting_suggestions = self._generate_targeting_suggestions(request)
|
||||
creative_suggestions = self._generate_creative_suggestions(request)
|
||||
optimization_tips = self._generate_optimization_tips(request)
|
||||
compliance_notes = self._generate_compliance_notes(request)
|
||||
budget_recommendations = self._generate_budget_recommendations(request, actual_budget)
|
||||
|
||||
return FacebookAdCopyResponse(
|
||||
success=True,
|
||||
primary_ad_copy=primary_copy,
|
||||
ad_variations=variations,
|
||||
targeting_suggestions=targeting_suggestions,
|
||||
creative_suggestions=creative_suggestions,
|
||||
performance_predictions=performance,
|
||||
optimization_tips=optimization_tips,
|
||||
compliance_notes=compliance_notes,
|
||||
budget_recommendations=budget_recommendations,
|
||||
metadata={
|
||||
"business_type": request.business_type,
|
||||
"objective": actual_objective,
|
||||
"format": request.ad_format.value,
|
||||
"budget": actual_budget
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookAdCopyResponse(
|
||||
**self._handle_error(e, "Facebook ad copy generation")
|
||||
)
|
||||
|
||||
def _generate_primary_ad_copy(self, request: FacebookAdCopyRequest, objective: str, age_group: str) -> Dict[str, str]:
|
||||
"""Generate the primary ad copy."""
|
||||
prompt = f"""
|
||||
Create a high-converting Facebook ad copy for:
|
||||
|
||||
Business: {request.business_type}
|
||||
Product/Service: {request.product_service}
|
||||
Objective: {objective}
|
||||
Format: {request.ad_format.value}
|
||||
Target Audience: {request.target_audience}
|
||||
Age Group: {age_group}
|
||||
|
||||
Unique Selling Proposition: {request.unique_selling_proposition}
|
||||
Offer Details: {request.offer_details or 'No specific offer'}
|
||||
Brand Voice: {request.brand_voice or 'Professional and engaging'}
|
||||
|
||||
Targeting Details:
|
||||
- Location: {request.targeting_options.location or 'Not specified'}
|
||||
- Interests: {request.targeting_options.interests or 'Not specified'}
|
||||
- Behaviors: {request.targeting_options.behaviors or 'Not specified'}
|
||||
|
||||
Create ad copy with:
|
||||
1. Compelling headline (25 characters max)
|
||||
2. Primary text (125 characters max for optimal performance)
|
||||
3. Description (27 characters max)
|
||||
4. Strong call-to-action
|
||||
|
||||
Make it conversion-focused and compliant with Facebook ad policies.
|
||||
"""
|
||||
|
||||
try:
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"headline": {"type": "string"},
|
||||
"primary_text": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"call_to_action": {"type": "string"}
|
||||
}
|
||||
}
|
||||
|
||||
response = self._generate_structured_response(prompt, schema, temperature=0.6)
|
||||
|
||||
if isinstance(response, dict) and not response.get('error'):
|
||||
return response
|
||||
else:
|
||||
# Fallback to text generation
|
||||
content = self._generate_text(prompt, temperature=0.6)
|
||||
return self._parse_ad_copy_from_text(content)
|
||||
|
||||
except Exception:
|
||||
# Fallback to text generation
|
||||
content = self._generate_text(prompt, temperature=0.6)
|
||||
return self._parse_ad_copy_from_text(content)
|
||||
|
||||
def _generate_ad_variations(self, request: FacebookAdCopyRequest, objective: str, age_group: str) -> AdCopyVariations:
|
||||
"""Generate multiple variations for A/B testing."""
|
||||
prompt = f"""
|
||||
Create 3 variations each of headlines, primary text, descriptions, and CTAs for Facebook ads targeting:
|
||||
|
||||
Business: {request.business_type}
|
||||
Product/Service: {request.product_service}
|
||||
Objective: {objective}
|
||||
Target: {request.target_audience} ({age_group})
|
||||
|
||||
USP: {request.unique_selling_proposition}
|
||||
|
||||
Create variations that test different approaches:
|
||||
- Emotional vs. Logical appeals
|
||||
- Benefit-focused vs. Feature-focused
|
||||
- Urgency vs. Value-driven
|
||||
|
||||
Format as lists of 3 items each.
|
||||
"""
|
||||
|
||||
try:
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"headline_variations": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
"primary_text_variations": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
"description_variations": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
},
|
||||
"cta_variations": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = self._generate_structured_response(prompt, schema, temperature=0.7)
|
||||
|
||||
if isinstance(response, dict) and not response.get('error'):
|
||||
return AdCopyVariations(**response)
|
||||
else:
|
||||
return self._create_default_variations()
|
||||
|
||||
except Exception:
|
||||
return self._create_default_variations()
|
||||
|
||||
def _generate_performance_predictions(self, request: FacebookAdCopyRequest, budget: str) -> AdPerformancePredictions:
|
||||
"""Generate performance predictions based on budget and targeting."""
|
||||
# Simple logic based on budget and audience size
|
||||
if "Small" in budget or "$10-50" in budget:
|
||||
reach = "1K-5K"
|
||||
ctr = "1.2-2.5%"
|
||||
cpc = "$0.75-1.50"
|
||||
conversions = "15-40"
|
||||
score = "Good"
|
||||
elif "Medium" in budget or "$50-200" in budget:
|
||||
reach = "5K-20K"
|
||||
ctr = "1.5-3.0%"
|
||||
cpc = "$0.50-1.00"
|
||||
conversions = "50-150"
|
||||
score = "Very Good"
|
||||
else:
|
||||
reach = "20K-100K"
|
||||
ctr = "2.0-4.0%"
|
||||
cpc = "$0.30-0.80"
|
||||
conversions = "200-800"
|
||||
score = "Excellent"
|
||||
|
||||
return AdPerformancePredictions(
|
||||
estimated_reach=reach,
|
||||
estimated_ctr=ctr,
|
||||
estimated_cpc=cpc,
|
||||
estimated_conversions=conversions,
|
||||
optimization_score=score
|
||||
)
|
||||
|
||||
def _generate_targeting_suggestions(self, request: FacebookAdCopyRequest) -> List[str]:
|
||||
"""Generate additional targeting suggestions."""
|
||||
suggestions = []
|
||||
|
||||
if request.targeting_options.interests:
|
||||
suggestions.append("Consider expanding interests to related categories")
|
||||
|
||||
if request.targeting_options.lookalike_audience:
|
||||
suggestions.append("Test lookalike audiences at 1%, 2%, and 5% similarity")
|
||||
|
||||
suggestions.extend([
|
||||
"Add behavioral targeting based on purchase intent",
|
||||
"Consider excluding recent customers to focus on new prospects",
|
||||
"Test custom audiences from website visitors",
|
||||
"Use demographic targeting refinements"
|
||||
])
|
||||
|
||||
return suggestions
|
||||
|
||||
def _generate_creative_suggestions(self, request: FacebookAdCopyRequest) -> List[str]:
|
||||
"""Generate creative and visual suggestions."""
|
||||
suggestions = []
|
||||
|
||||
if request.ad_format.value == "Single image":
|
||||
suggestions.extend([
|
||||
"Use high-quality, eye-catching visuals",
|
||||
"Include product in lifestyle context",
|
||||
"Test different color schemes"
|
||||
])
|
||||
elif request.ad_format.value == "Carousel":
|
||||
suggestions.extend([
|
||||
"Show different product angles or features",
|
||||
"Tell a story across carousel cards",
|
||||
"Include customer testimonials"
|
||||
])
|
||||
elif request.ad_format.value == "Single video":
|
||||
suggestions.extend([
|
||||
"Keep video under 15 seconds for best performance",
|
||||
"Include captions for sound-off viewing",
|
||||
"Start with attention-grabbing first 3 seconds"
|
||||
])
|
||||
|
||||
suggestions.extend([
|
||||
"Ensure mobile-first design approach",
|
||||
"Include social proof elements",
|
||||
"Test user-generated content"
|
||||
])
|
||||
|
||||
return suggestions
|
||||
|
||||
def _generate_optimization_tips(self, request: FacebookAdCopyRequest) -> List[str]:
|
||||
"""Generate optimization tips."""
|
||||
return [
|
||||
"Test different ad placements (feed, stories, reels)",
|
||||
"Use automatic placements initially, then optimize",
|
||||
"Monitor frequency and refresh creative if >3",
|
||||
"A/B test audiences with 70% overlap maximum",
|
||||
"Set up conversion tracking for accurate measurement",
|
||||
"Use broad targeting to leverage Facebook's AI",
|
||||
"Schedule ads for peak audience activity times"
|
||||
]
|
||||
|
||||
def _generate_compliance_notes(self, request: FacebookAdCopyRequest) -> List[str]:
|
||||
"""Generate compliance and policy notes."""
|
||||
notes = [
|
||||
"Ensure all claims are substantiated and truthful",
|
||||
"Avoid excessive capitalization or punctuation",
|
||||
"Don't use misleading or exaggerated language"
|
||||
]
|
||||
|
||||
if "health" in request.business_type.lower() or "fitness" in request.business_type.lower():
|
||||
notes.extend([
|
||||
"Health claims require proper disclaimers",
|
||||
"Avoid before/after images without context"
|
||||
])
|
||||
|
||||
if "finance" in request.business_type.lower():
|
||||
notes.extend([
|
||||
"Financial services ads require additional compliance",
|
||||
"Include proper risk disclosures"
|
||||
])
|
||||
|
||||
return notes
|
||||
|
||||
def _generate_budget_recommendations(self, request: FacebookAdCopyRequest, budget: str) -> List[str]:
|
||||
"""Generate budget allocation recommendations."""
|
||||
recommendations = [
|
||||
"Start with automatic bidding for optimal results",
|
||||
"Set daily budget 5-10x your target CPA",
|
||||
"Allow 3-7 days for Facebook's learning phase"
|
||||
]
|
||||
|
||||
if "Small" in budget:
|
||||
recommendations.extend([
|
||||
"Focus on one audience segment initially",
|
||||
"Use conversion optimization once you have 50+ conversions/week"
|
||||
])
|
||||
else:
|
||||
recommendations.extend([
|
||||
"Split budget across 2-3 audience segments",
|
||||
"Allocate 70% to best-performing ads",
|
||||
"Reserve 30% for testing new creative"
|
||||
])
|
||||
|
||||
return recommendations
|
||||
|
||||
def _parse_ad_copy_from_text(self, content: str) -> Dict[str, str]:
|
||||
"""Parse ad copy components from generated text."""
|
||||
# Basic parsing - in production, you'd want more sophisticated parsing
|
||||
lines = content.split('\n')
|
||||
|
||||
return {
|
||||
"headline": "Discover Amazing Results Today!",
|
||||
"primary_text": "Transform your life with our proven solution. Join thousands of satisfied customers who've seen incredible results.",
|
||||
"description": "Limited time offer - Act now!",
|
||||
"call_to_action": "Learn More"
|
||||
}
|
||||
|
||||
def _create_default_variations(self) -> AdCopyVariations:
|
||||
"""Create default variations as fallback."""
|
||||
return AdCopyVariations(
|
||||
headline_variations=[
|
||||
"Get Results Fast",
|
||||
"Transform Your Life",
|
||||
"Limited Time Offer"
|
||||
],
|
||||
primary_text_variations=[
|
||||
"Join thousands who've achieved success",
|
||||
"Discover the solution you've been looking for",
|
||||
"Don't miss out on this opportunity"
|
||||
],
|
||||
description_variations=[
|
||||
"Act now - limited time",
|
||||
"Free trial available",
|
||||
"Money-back guarantee"
|
||||
],
|
||||
cta_variations=[
|
||||
"Learn More",
|
||||
"Get Started",
|
||||
"Claim Offer"
|
||||
]
|
||||
)
|
||||
281
backend/api/facebook_writer/services/base_service.py
Normal file
281
backend/api/facebook_writer/services/base_service.py
Normal file
@@ -0,0 +1,281 @@
|
||||
"""Base service for Facebook Writer functionality."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
from loguru import logger
|
||||
|
||||
# Add the backend path to sys.path to import services
|
||||
backend_path = Path(__file__).parent.parent.parent.parent
|
||||
sys.path.append(str(backend_path))
|
||||
|
||||
from services.llm_providers.gemini_provider import gemini_text_response, gemini_structured_json_response
|
||||
from services.persona_analysis_service import PersonaAnalysisService
|
||||
from typing import Dict, Any, Optional
|
||||
import time
|
||||
|
||||
|
||||
class FacebookWriterBaseService:
|
||||
"""Base service class for Facebook Writer functionality."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the base service."""
|
||||
self.logger = logger
|
||||
self.persona_service = PersonaAnalysisService()
|
||||
|
||||
# Persona caching
|
||||
self._persona_cache: Dict[str, Dict[str, Any]] = {}
|
||||
self._cache_timestamps: Dict[str, float] = {}
|
||||
self._cache_duration = 300 # 5 minutes cache duration
|
||||
|
||||
def _generate_text(self, prompt: str, temperature: float = 0.7, max_tokens: int = 2048) -> str:
|
||||
"""
|
||||
Generate text using Gemini provider.
|
||||
|
||||
Args:
|
||||
prompt: The prompt to send to the AI
|
||||
temperature: Control randomness of output
|
||||
max_tokens: Maximum tokens in response
|
||||
|
||||
Returns:
|
||||
Generated text response
|
||||
"""
|
||||
try:
|
||||
response = gemini_text_response(
|
||||
prompt=prompt,
|
||||
temperature=temperature,
|
||||
top_p=0.9,
|
||||
n=40,
|
||||
max_tokens=max_tokens,
|
||||
system_prompt=None
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error generating text: {e}")
|
||||
raise
|
||||
|
||||
def _generate_structured_response(
|
||||
self,
|
||||
prompt: str,
|
||||
schema: Dict[str, Any],
|
||||
temperature: float = 0.3,
|
||||
max_tokens: int = 8192
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Generate structured JSON response using Gemini provider.
|
||||
|
||||
Args:
|
||||
prompt: The prompt to send to the AI
|
||||
schema: JSON schema for structured output
|
||||
temperature: Control randomness (lower for structured output)
|
||||
max_tokens: Maximum tokens in response
|
||||
|
||||
Returns:
|
||||
Structured JSON response
|
||||
"""
|
||||
try:
|
||||
response = gemini_structured_json_response(
|
||||
prompt=prompt,
|
||||
schema=schema,
|
||||
temperature=temperature,
|
||||
top_p=0.9,
|
||||
top_k=40,
|
||||
max_tokens=max_tokens,
|
||||
system_prompt=None
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error generating structured response: {e}")
|
||||
raise
|
||||
|
||||
def _build_base_prompt(self, business_type: str, target_audience: str, purpose: str) -> str:
|
||||
"""
|
||||
Build a base prompt for Facebook content generation.
|
||||
|
||||
Args:
|
||||
business_type: Type of business
|
||||
target_audience: Target audience description
|
||||
purpose: Purpose or goal of the content
|
||||
|
||||
Returns:
|
||||
Base prompt string
|
||||
"""
|
||||
return f"""
|
||||
You are an expert Facebook content creator specializing in creating engaging, high-performing social media content.
|
||||
|
||||
Business Context:
|
||||
- Business Type: {business_type}
|
||||
- Target Audience: {target_audience}
|
||||
- Content Purpose: {purpose}
|
||||
|
||||
Create content that:
|
||||
1. Resonates with the target audience
|
||||
2. Aligns with Facebook's best practices
|
||||
3. Encourages engagement and interaction
|
||||
4. Maintains a professional yet approachable tone
|
||||
5. Includes relevant calls-to-action when appropriate
|
||||
"""
|
||||
|
||||
def _create_analytics_prediction(self) -> Dict[str, str]:
|
||||
"""
|
||||
Create default analytics predictions.
|
||||
|
||||
Returns:
|
||||
Dictionary with analytics predictions
|
||||
"""
|
||||
return {
|
||||
"expected_reach": "2.5K - 5K",
|
||||
"expected_engagement": "5-8%",
|
||||
"best_time_to_post": "2 PM - 4 PM"
|
||||
}
|
||||
|
||||
def _create_optimization_suggestions(self, content_type: str = "post") -> list:
|
||||
"""
|
||||
Create default optimization suggestions.
|
||||
|
||||
Args:
|
||||
content_type: Type of content being optimized
|
||||
|
||||
Returns:
|
||||
List of optimization suggestions
|
||||
"""
|
||||
base_suggestions = [
|
||||
"Consider adding a question to increase comments",
|
||||
"Use more emojis to increase visibility",
|
||||
"Keep paragraphs shorter for better readability"
|
||||
]
|
||||
|
||||
if content_type == "post":
|
||||
base_suggestions.append("Add a poll to increase engagement")
|
||||
elif content_type == "story":
|
||||
base_suggestions.append("Include interactive stickers")
|
||||
elif content_type == "reel":
|
||||
base_suggestions.append("Use trending music for better reach")
|
||||
|
||||
return base_suggestions
|
||||
|
||||
def _get_persona_data(self, user_id: int = 1) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get persona data for Facebook platform with caching.
|
||||
|
||||
Args:
|
||||
user_id: User ID to get persona for
|
||||
|
||||
Returns:
|
||||
Persona data or None if not available
|
||||
"""
|
||||
cache_key = f"facebook_persona_{user_id}"
|
||||
current_time = time.time()
|
||||
|
||||
# Check cache first
|
||||
if cache_key in self._persona_cache and cache_key in self._cache_timestamps:
|
||||
cache_age = current_time - self._cache_timestamps[cache_key]
|
||||
if cache_age < self._cache_duration:
|
||||
self.logger.debug(f"Using cached persona data for user {user_id} (age: {cache_age:.1f}s)")
|
||||
return self._persona_cache[cache_key]
|
||||
else:
|
||||
# Cache expired, remove it
|
||||
self.logger.debug(f"Cache expired for user {user_id}, refreshing...")
|
||||
del self._persona_cache[cache_key]
|
||||
del self._cache_timestamps[cache_key]
|
||||
|
||||
# Fetch fresh data
|
||||
try:
|
||||
persona_data = self.persona_service.get_persona_for_platform(user_id, 'facebook')
|
||||
|
||||
# Cache the result
|
||||
if persona_data:
|
||||
self._persona_cache[cache_key] = persona_data
|
||||
self._cache_timestamps[cache_key] = current_time
|
||||
self.logger.debug(f"Cached persona data for user {user_id}")
|
||||
|
||||
return persona_data
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Could not load persona data for Facebook content generation: {e}")
|
||||
return None
|
||||
|
||||
def _clear_persona_cache(self, user_id: int = None):
|
||||
"""
|
||||
Clear persona cache for a specific user or all users.
|
||||
|
||||
Args:
|
||||
user_id: User ID to clear cache for, or None to clear all
|
||||
"""
|
||||
if user_id is None:
|
||||
self._persona_cache.clear()
|
||||
self._cache_timestamps.clear()
|
||||
self.logger.info("Cleared all persona cache")
|
||||
else:
|
||||
cache_key = f"facebook_persona_{user_id}"
|
||||
if cache_key in self._persona_cache:
|
||||
del self._persona_cache[cache_key]
|
||||
del self._cache_timestamps[cache_key]
|
||||
self.logger.info(f"Cleared persona cache for user {user_id}")
|
||||
|
||||
def _build_persona_enhanced_prompt(self, base_prompt: str, persona_data: Optional[Dict[str, Any]] = None) -> str:
|
||||
"""
|
||||
Enhance prompt with persona data if available.
|
||||
|
||||
Args:
|
||||
base_prompt: Base prompt to enhance
|
||||
persona_data: Persona data to incorporate
|
||||
|
||||
Returns:
|
||||
Enhanced prompt with persona guidance
|
||||
"""
|
||||
if not persona_data:
|
||||
return base_prompt
|
||||
|
||||
try:
|
||||
core_persona = persona_data.get('core_persona', {})
|
||||
platform_persona = persona_data.get('platform_adaptation', {})
|
||||
|
||||
if not core_persona:
|
||||
return base_prompt
|
||||
|
||||
persona_guidance = f"""
|
||||
PERSONA-AWARE WRITING GUIDANCE:
|
||||
- PERSONA: {core_persona.get('persona_name', 'Unknown')} ({core_persona.get('archetype', 'Unknown')})
|
||||
- CORE BELIEF: {core_persona.get('core_belief', 'Unknown')}
|
||||
- CONFIDENCE SCORE: {core_persona.get('confidence_score', 0)}%
|
||||
|
||||
PLATFORM OPTIMIZATION (Facebook):
|
||||
- CHARACTER LIMIT: {platform_persona.get('content_format_rules', {}).get('character_limit', '63206')} characters
|
||||
- OPTIMAL LENGTH: {platform_persona.get('content_format_rules', {}).get('optimal_length', '40-80 characters')}
|
||||
- ENGAGEMENT PATTERN: {platform_persona.get('engagement_patterns', {}).get('posting_frequency', '1-2 times per day')}
|
||||
- HASHTAG STRATEGY: {platform_persona.get('lexical_features', {}).get('hashtag_strategy', '1-2 relevant hashtags')}
|
||||
|
||||
ALWAYS generate content that matches this persona's linguistic fingerprint and platform optimization rules.
|
||||
"""
|
||||
|
||||
return f"{base_prompt}\n\n{persona_guidance}"
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"Error enhancing prompt with persona data: {e}")
|
||||
return base_prompt
|
||||
|
||||
def _handle_error(self, error: Exception, operation: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Handle errors and return standardized error response.
|
||||
|
||||
Args:
|
||||
error: The exception that occurred
|
||||
operation: Description of the operation that failed
|
||||
|
||||
Returns:
|
||||
Standardized error response
|
||||
"""
|
||||
error_message = f"Error in {operation}: {str(error)}"
|
||||
self.logger.error(error_message)
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": error_message,
|
||||
"content": None,
|
||||
"metadata": {
|
||||
"operation": operation,
|
||||
"error_type": type(error).__name__
|
||||
}
|
||||
}
|
||||
125
backend/api/facebook_writer/services/post_service.py
Normal file
125
backend/api/facebook_writer/services/post_service.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""Facebook Post generation service."""
|
||||
|
||||
from typing import Dict, Any
|
||||
from ..models.post_models import FacebookPostRequest, FacebookPostResponse, FacebookPostAnalytics, FacebookPostOptimization
|
||||
from .base_service import FacebookWriterBaseService
|
||||
|
||||
|
||||
class FacebookPostService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook posts."""
|
||||
|
||||
def generate_post(self, request: FacebookPostRequest) -> FacebookPostResponse:
|
||||
"""
|
||||
Generate a Facebook post based on the request parameters.
|
||||
|
||||
Args:
|
||||
request: FacebookPostRequest containing all the parameters
|
||||
|
||||
Returns:
|
||||
FacebookPostResponse with the generated content
|
||||
"""
|
||||
try:
|
||||
# Determine the actual goal and tone
|
||||
actual_goal = request.custom_goal if request.post_goal.value == "Custom" else request.post_goal.value
|
||||
actual_tone = request.custom_tone if request.post_tone.value == "Custom" else request.post_tone.value
|
||||
|
||||
# Get persona data for enhanced content generation
|
||||
# Beta testing: Force user_id=1 for all requests
|
||||
user_id = 1
|
||||
persona_data = self._get_persona_data(user_id)
|
||||
|
||||
# Build the prompt
|
||||
base_prompt = self._build_post_prompt(request, actual_goal, actual_tone)
|
||||
prompt = self._build_persona_enhanced_prompt(base_prompt, persona_data)
|
||||
|
||||
# Generate the post content
|
||||
content = self._generate_text(prompt, temperature=0.7, max_tokens=1024)
|
||||
|
||||
if not content:
|
||||
return FacebookPostResponse(
|
||||
success=False,
|
||||
error="Failed to generate post content"
|
||||
)
|
||||
|
||||
# Create analytics and optimization suggestions
|
||||
analytics = FacebookPostAnalytics(
|
||||
expected_reach="2.5K - 5K",
|
||||
expected_engagement="5-8%",
|
||||
best_time_to_post="2 PM - 4 PM"
|
||||
)
|
||||
|
||||
optimization = FacebookPostOptimization(
|
||||
suggestions=self._create_optimization_suggestions("post")
|
||||
)
|
||||
|
||||
return FacebookPostResponse(
|
||||
success=True,
|
||||
content=content,
|
||||
analytics=analytics,
|
||||
optimization=optimization,
|
||||
metadata={
|
||||
"business_type": request.business_type,
|
||||
"target_audience": request.target_audience,
|
||||
"goal": actual_goal,
|
||||
"tone": actual_tone
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookPostResponse(
|
||||
**self._handle_error(e, "Facebook post generation")
|
||||
)
|
||||
|
||||
def _build_post_prompt(self, request: FacebookPostRequest, goal: str, tone: str) -> str:
|
||||
"""
|
||||
Build the prompt for Facebook post generation.
|
||||
|
||||
Args:
|
||||
request: The post request
|
||||
goal: The actual goal (resolved from custom if needed)
|
||||
tone: The actual tone (resolved from custom if needed)
|
||||
|
||||
Returns:
|
||||
Formatted prompt string
|
||||
"""
|
||||
base_prompt = self._build_base_prompt(
|
||||
request.business_type,
|
||||
request.target_audience,
|
||||
goal
|
||||
)
|
||||
|
||||
prompt = f"""
|
||||
{base_prompt}
|
||||
|
||||
Generate a Facebook post with the following specifications:
|
||||
|
||||
Goal: {goal}
|
||||
Tone: {tone}
|
||||
|
||||
Content Requirements:
|
||||
- Include: {request.include or 'N/A'}
|
||||
- Avoid: {request.avoid or 'N/A'}
|
||||
|
||||
Advanced Options:
|
||||
- Use attention-grabbing hook: {request.advanced_options.use_hook}
|
||||
- Include storytelling elements: {request.advanced_options.use_story}
|
||||
- Add clear call-to-action: {request.advanced_options.use_cta}
|
||||
- Include engagement question: {request.advanced_options.use_question}
|
||||
- Use relevant emojis: {request.advanced_options.use_emoji}
|
||||
- Add relevant hashtags: {request.advanced_options.use_hashtags}
|
||||
|
||||
Media Type: {request.media_type.value}
|
||||
|
||||
Please write a well-structured Facebook post that:
|
||||
1. Grabs attention in the first line (hook)
|
||||
2. Maintains consistent {tone} tone throughout
|
||||
3. Includes engaging content that aligns with the goal: {goal}
|
||||
4. Ends with a clear call-to-action (if enabled)
|
||||
5. Uses appropriate formatting and emojis (if enabled)
|
||||
6. Includes relevant hashtags (if enabled)
|
||||
7. Considers the target audience: {request.target_audience}
|
||||
|
||||
The post should be engaging, platform-appropriate, and optimized for Facebook's algorithm.
|
||||
"""
|
||||
|
||||
return prompt
|
||||
322
backend/api/facebook_writer/services/remaining_services.py
Normal file
322
backend/api/facebook_writer/services/remaining_services.py
Normal file
@@ -0,0 +1,322 @@
|
||||
"""Remaining Facebook Writer services - placeholder implementations."""
|
||||
|
||||
from typing import Dict, Any, List
|
||||
from ..models import *
|
||||
from ..models.carousel_models import CarouselSlide
|
||||
from .base_service import FacebookWriterBaseService
|
||||
|
||||
|
||||
class FacebookReelService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook reels."""
|
||||
|
||||
def generate_reel(self, request: FacebookReelRequest) -> FacebookReelResponse:
|
||||
"""Generate a Facebook reel script."""
|
||||
try:
|
||||
actual_reel_type = request.custom_reel_type if request.reel_type.value == "Custom" else request.reel_type.value
|
||||
actual_style = request.custom_style if request.reel_style.value == "Custom" else request.reel_style.value
|
||||
|
||||
# Get persona data for enhanced content generation
|
||||
# Beta testing: Force user_id=1 for all requests
|
||||
user_id = 1
|
||||
persona_data = self._get_persona_data(user_id)
|
||||
|
||||
base_prompt = f"""
|
||||
Create a Facebook Reel script for:
|
||||
Business: {request.business_type}
|
||||
Audience: {request.target_audience}
|
||||
Type: {actual_reel_type}
|
||||
Length: {request.reel_length.value}
|
||||
Style: {actual_style}
|
||||
Topic: {request.topic}
|
||||
Include: {request.include or 'N/A'}
|
||||
Avoid: {request.avoid or 'N/A'}
|
||||
Music: {request.music_preference or 'Trending'}
|
||||
|
||||
Create an engaging reel script with scene breakdown, timing, and music suggestions.
|
||||
"""
|
||||
|
||||
prompt = self._build_persona_enhanced_prompt(base_prompt, persona_data)
|
||||
content = self._generate_text(prompt, temperature=0.7, max_tokens=1024)
|
||||
|
||||
return FacebookReelResponse(
|
||||
success=True,
|
||||
script=content,
|
||||
scene_breakdown=["Opening hook", "Main content", "Call to action"],
|
||||
music_suggestions=["Trending pop", "Upbeat instrumental", "Viral sound"],
|
||||
hashtag_suggestions=["#Reels", "#Trending", "#Business"],
|
||||
engagement_tips=self._create_optimization_suggestions("reel")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookReelResponse(**self._handle_error(e, "Facebook reel generation"))
|
||||
|
||||
|
||||
class FacebookCarouselService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook carousels."""
|
||||
|
||||
def generate_carousel(self, request: FacebookCarouselRequest) -> FacebookCarouselResponse:
|
||||
"""Generate a Facebook carousel post."""
|
||||
try:
|
||||
actual_type = request.custom_carousel_type if request.carousel_type.value == "Custom" else request.carousel_type.value
|
||||
|
||||
prompt = f"""
|
||||
Create a Facebook Carousel post for:
|
||||
Business: {request.business_type}
|
||||
Audience: {request.target_audience}
|
||||
Type: {actual_type}
|
||||
Topic: {request.topic}
|
||||
Slides: {request.num_slides}
|
||||
CTA: {request.cta_text or 'Learn More'}
|
||||
Include: {request.include or 'N/A'}
|
||||
Avoid: {request.avoid or 'N/A'}
|
||||
|
||||
Create engaging carousel content with main caption and individual slide content.
|
||||
"""
|
||||
|
||||
content = self._generate_text(prompt, temperature=0.7, max_tokens=1024)
|
||||
|
||||
# Create sample slides
|
||||
slides = []
|
||||
for i in range(request.num_slides):
|
||||
slides.append(CarouselSlide(
|
||||
title=f"Slide {i+1} Title",
|
||||
content=f"Engaging content for slide {i+1}",
|
||||
image_description=f"Visual description for slide {i+1}"
|
||||
))
|
||||
|
||||
return FacebookCarouselResponse(
|
||||
success=True,
|
||||
main_caption=content,
|
||||
slides=slides,
|
||||
design_suggestions=["Use consistent color scheme", "Include brand elements"],
|
||||
hashtag_suggestions=["#Carousel", "#Business", "#Marketing"],
|
||||
engagement_tips=self._create_optimization_suggestions("carousel")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookCarouselResponse(**self._handle_error(e, "Facebook carousel generation"))
|
||||
|
||||
|
||||
class FacebookEventService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook events."""
|
||||
|
||||
def generate_event(self, request: FacebookEventRequest) -> FacebookEventResponse:
|
||||
"""Generate a Facebook event description."""
|
||||
try:
|
||||
actual_type = request.custom_event_type if request.event_type.value == "Custom" else request.event_type.value
|
||||
|
||||
prompt = f"""
|
||||
Create a Facebook Event description for:
|
||||
Event: {request.event_name}
|
||||
Type: {actual_type}
|
||||
Format: {request.event_format.value}
|
||||
Business: {request.business_type}
|
||||
Audience: {request.target_audience}
|
||||
Date: {request.event_date or 'TBD'}
|
||||
Location: {request.location or 'TBD'}
|
||||
Benefits: {request.key_benefits or 'N/A'}
|
||||
Speakers: {request.speakers or 'N/A'}
|
||||
|
||||
Create compelling event description that drives attendance.
|
||||
"""
|
||||
|
||||
content = self._generate_text(prompt, temperature=0.7, max_tokens=1024)
|
||||
|
||||
return FacebookEventResponse(
|
||||
success=True,
|
||||
event_title=request.event_name,
|
||||
event_description=content,
|
||||
short_description=content[:155] if content else None,
|
||||
key_highlights=["Expert speakers", "Networking opportunities", "Valuable insights"],
|
||||
call_to_action="Register Now",
|
||||
hashtag_suggestions=["#Event", "#Business", "#Networking"],
|
||||
promotion_tips=["Share in relevant groups", "Create countdown posts", "Partner with influencers"]
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookEventResponse(**self._handle_error(e, "Facebook event generation"))
|
||||
|
||||
|
||||
class FacebookHashtagService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook hashtags."""
|
||||
|
||||
def generate_hashtags(self, request: FacebookHashtagRequest) -> FacebookHashtagResponse:
|
||||
"""Generate relevant hashtags."""
|
||||
try:
|
||||
actual_purpose = request.custom_purpose if request.purpose.value == "Custom" else request.purpose.value
|
||||
|
||||
# Generate basic hashtags based on business type and topic
|
||||
hashtags = []
|
||||
|
||||
# Business-related hashtags
|
||||
business_tags = [f"#{request.business_type.replace(' ', '')}", f"#{request.industry.replace(' ', '')}"]
|
||||
hashtags.extend(business_tags)
|
||||
|
||||
# Topic-related hashtags
|
||||
topic_words = request.content_topic.split()
|
||||
topic_tags = [f"#{word.capitalize()}" for word in topic_words if len(word) > 3]
|
||||
hashtags.extend(topic_tags[:5])
|
||||
|
||||
# Generic engagement hashtags
|
||||
generic_tags = ["#Business", "#Marketing", "#Growth", "#Success", "#Community"]
|
||||
hashtags.extend(generic_tags)
|
||||
|
||||
# Location hashtags if provided
|
||||
if request.location:
|
||||
location_tag = f"#{request.location.replace(' ', '').replace(',', '')}"
|
||||
hashtags.append(location_tag)
|
||||
|
||||
# Limit to requested count
|
||||
hashtags = hashtags[:request.hashtag_count]
|
||||
|
||||
return FacebookHashtagResponse(
|
||||
success=True,
|
||||
hashtags=hashtags,
|
||||
categorized_hashtags={
|
||||
"business": business_tags,
|
||||
"topic": topic_tags,
|
||||
"generic": generic_tags
|
||||
},
|
||||
trending_hashtags=["#Trending", "#Viral", "#Popular"],
|
||||
usage_tips=["Mix popular and niche hashtags", "Keep hashtags relevant", "Update regularly"],
|
||||
performance_predictions={"reach": "Medium", "engagement": "Good"}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookHashtagResponse(**self._handle_error(e, "Facebook hashtag generation"))
|
||||
|
||||
|
||||
class FacebookEngagementService(FacebookWriterBaseService):
|
||||
"""Service for analyzing Facebook engagement."""
|
||||
|
||||
def analyze_engagement(self, request: FacebookEngagementRequest) -> FacebookEngagementResponse:
|
||||
"""Analyze content for engagement potential."""
|
||||
try:
|
||||
# Simple content analysis
|
||||
content_length = len(request.content)
|
||||
word_count = len(request.content.split())
|
||||
|
||||
# Calculate basic scores
|
||||
length_score = min(100, (content_length / 10)) # Optimal around 1000 chars
|
||||
word_score = min(100, (word_count / 2)) # Optimal around 200 words
|
||||
|
||||
overall_score = (length_score + word_score) / 2
|
||||
|
||||
metrics = EngagementMetrics(
|
||||
predicted_reach="2K-8K",
|
||||
predicted_engagement_rate="3-7%",
|
||||
predicted_likes="50-200",
|
||||
predicted_comments="10-50",
|
||||
predicted_shares="5-25",
|
||||
virality_score="Medium"
|
||||
)
|
||||
|
||||
optimization = OptimizationSuggestions(
|
||||
content_improvements=["Add more emojis", "Include questions", "Shorten paragraphs"],
|
||||
timing_suggestions=["Post between 2-4 PM", "Avoid late nights", "Test weekends"],
|
||||
hashtag_improvements=["Use trending hashtags", "Mix popular and niche", "Limit to 5-7 hashtags"],
|
||||
visual_suggestions=["Add compelling image", "Use bright colors", "Include text overlay"],
|
||||
engagement_tactics=["Ask questions", "Create polls", "Encourage sharing"]
|
||||
)
|
||||
|
||||
return FacebookEngagementResponse(
|
||||
success=True,
|
||||
content_score=overall_score,
|
||||
engagement_metrics=metrics,
|
||||
optimization_suggestions=optimization,
|
||||
sentiment_analysis={"tone": "positive", "emotion": "neutral"},
|
||||
trend_alignment={"score": "good", "trending_topics": ["business", "growth"]},
|
||||
competitor_insights={"performance": "average", "opportunities": ["better visuals", "more interactive"]}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookEngagementResponse(**self._handle_error(e, "Facebook engagement analysis"))
|
||||
|
||||
|
||||
class FacebookGroupPostService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook group posts."""
|
||||
|
||||
def generate_group_post(self, request: FacebookGroupPostRequest) -> FacebookGroupPostResponse:
|
||||
"""Generate a Facebook group post."""
|
||||
try:
|
||||
actual_type = request.custom_group_type if request.group_type.value == "Custom" else request.group_type.value
|
||||
actual_purpose = request.custom_purpose if request.post_purpose.value == "Custom" else request.post_purpose.value
|
||||
|
||||
prompt = f"""
|
||||
Create a Facebook Group post for:
|
||||
Group: {request.group_name} ({actual_type})
|
||||
Purpose: {actual_purpose}
|
||||
Business: {request.business_type}
|
||||
Topic: {request.topic}
|
||||
Audience: {request.target_audience}
|
||||
Value: {request.value_proposition}
|
||||
|
||||
Rules to follow:
|
||||
- No promotion: {request.group_rules.no_promotion}
|
||||
- Value first: {request.group_rules.value_first}
|
||||
- No links: {request.group_rules.no_links}
|
||||
- Community focused: {request.group_rules.community_focused}
|
||||
|
||||
Create a post that provides value and follows group guidelines.
|
||||
"""
|
||||
|
||||
content = self._generate_text(prompt, temperature=0.7, max_tokens=1024)
|
||||
|
||||
return FacebookGroupPostResponse(
|
||||
success=True,
|
||||
content=content,
|
||||
engagement_starters=["What's your experience with this?", "How do you handle this situation?"],
|
||||
value_highlights=["Free insights", "Actionable tips", "Community support"],
|
||||
community_guidelines=["Provides value first", "Encourages discussion", "Follows group rules"],
|
||||
follow_up_suggestions=["Respond to comments promptly", "Share additional resources", "Connect with commenters"],
|
||||
relationship_building_tips=["Be authentic", "Help others", "Participate regularly"]
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookGroupPostResponse(**self._handle_error(e, "Facebook group post generation"))
|
||||
|
||||
|
||||
class FacebookPageAboutService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook page about sections."""
|
||||
|
||||
def generate_page_about(self, request: FacebookPageAboutRequest) -> FacebookPageAboutResponse:
|
||||
"""Generate a Facebook page about section."""
|
||||
try:
|
||||
actual_category = request.custom_category if request.business_category.value == "Custom" else request.business_category.value
|
||||
actual_tone = request.custom_tone if request.page_tone.value == "Custom" else request.page_tone.value
|
||||
|
||||
prompt = f"""
|
||||
Create a Facebook Page About section for:
|
||||
Business: {request.business_name}
|
||||
Category: {actual_category}
|
||||
Description: {request.business_description}
|
||||
Audience: {request.target_audience}
|
||||
USP: {request.unique_value_proposition}
|
||||
Services: {request.services_products}
|
||||
Tone: {actual_tone}
|
||||
|
||||
History: {request.company_history or 'N/A'}
|
||||
Mission: {request.mission_vision or 'N/A'}
|
||||
Achievements: {request.achievements or 'N/A'}
|
||||
Keywords: {request.keywords or 'N/A'}
|
||||
|
||||
Create professional page content including short and long descriptions.
|
||||
"""
|
||||
|
||||
content = self._generate_text(prompt, temperature=0.6, max_tokens=1024)
|
||||
|
||||
return FacebookPageAboutResponse(
|
||||
success=True,
|
||||
short_description=f"{request.business_name} - {request.business_description}"[:155],
|
||||
long_description=content,
|
||||
company_overview=f"Leading {actual_category} business serving {request.target_audience}",
|
||||
mission_statement=request.mission_vision or f"To provide excellent {request.services_products} to our community",
|
||||
story_section=request.company_history or "Our journey began with a vision to make a difference",
|
||||
services_section=f"We specialize in {request.services_products}",
|
||||
cta_suggestions=["Contact Us", "Learn More", "Get Quote"],
|
||||
keyword_optimization=["business", "service", "quality", "professional"],
|
||||
completion_tips=["Add contact info", "Upload cover photo", "Create call-to-action button"]
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookPageAboutResponse(**self._handle_error(e, "Facebook page about generation"))
|
||||
243
backend/api/facebook_writer/services/story_service.py
Normal file
243
backend/api/facebook_writer/services/story_service.py
Normal file
@@ -0,0 +1,243 @@
|
||||
"""Facebook Story generation service."""
|
||||
|
||||
from typing import Dict, Any, List
|
||||
from ..models.story_models import FacebookStoryRequest, FacebookStoryResponse
|
||||
from .base_service import FacebookWriterBaseService
|
||||
try:
|
||||
from ...services.llm_providers.main_image_generation import generate_image
|
||||
from base64 import b64encode
|
||||
except Exception:
|
||||
generate_image = None # type: ignore
|
||||
b64encode = None # type: ignore
|
||||
|
||||
|
||||
class FacebookStoryService(FacebookWriterBaseService):
|
||||
"""Service for generating Facebook stories."""
|
||||
|
||||
def generate_story(self, request: FacebookStoryRequest) -> FacebookStoryResponse:
|
||||
"""
|
||||
Generate a Facebook story based on the request parameters.
|
||||
|
||||
Args:
|
||||
request: FacebookStoryRequest containing all the parameters
|
||||
|
||||
Returns:
|
||||
FacebookStoryResponse with the generated content
|
||||
"""
|
||||
try:
|
||||
# Determine the actual story type and tone
|
||||
actual_story_type = request.custom_story_type if request.story_type.value == "Custom" else request.story_type.value
|
||||
actual_tone = request.custom_tone if request.story_tone.value == "Custom" else request.story_tone.value
|
||||
|
||||
# Get persona data for enhanced content generation
|
||||
# Beta testing: Force user_id=1 for all requests
|
||||
user_id = 1
|
||||
persona_data = self._get_persona_data(user_id)
|
||||
|
||||
# Build the prompt
|
||||
base_prompt = self._build_story_prompt(request, actual_story_type, actual_tone)
|
||||
prompt = self._build_persona_enhanced_prompt(base_prompt, persona_data)
|
||||
|
||||
# Generate the story content
|
||||
content = self._generate_text(prompt, temperature=0.7, max_tokens=1024)
|
||||
|
||||
if not content:
|
||||
return FacebookStoryResponse(
|
||||
success=False,
|
||||
error="Failed to generate story content"
|
||||
)
|
||||
|
||||
# Generate visual suggestions and engagement tips
|
||||
visual_suggestions = self._generate_visual_suggestions(actual_story_type, request.visual_options)
|
||||
engagement_tips = self._generate_engagement_tips("story")
|
||||
# Optional: generate one story image (9:16) using unified image generation
|
||||
images_base64: List[str] = []
|
||||
try:
|
||||
if generate_image is not None and b64encode is not None:
|
||||
img_prompt = request.visual_options.background_image_prompt or (
|
||||
f"Facebook story background for {request.business_type}. "
|
||||
f"Style: {actual_tone}. Type: {actual_story_type}. Vertical mobile 9:16, high contrast, legible overlay space."
|
||||
)
|
||||
# Generate image using unified system (9:16 aspect ratio = 1080x1920)
|
||||
result = generate_image(
|
||||
prompt=img_prompt,
|
||||
options={
|
||||
"provider": "gemini", # Facebook stories use Gemini
|
||||
"width": 1080,
|
||||
"height": 1920,
|
||||
}
|
||||
)
|
||||
if result and result.image_bytes:
|
||||
# Convert bytes to base64
|
||||
image_b64 = b64encode(result.image_bytes).decode('utf-8')
|
||||
images_base64 = [image_b64]
|
||||
except Exception as e:
|
||||
# Log error but continue without images
|
||||
images_base64 = []
|
||||
|
||||
return FacebookStoryResponse(
|
||||
success=True,
|
||||
content=content,
|
||||
images_base64=images_base64[:1],
|
||||
visual_suggestions=visual_suggestions,
|
||||
engagement_tips=engagement_tips,
|
||||
metadata={
|
||||
"business_type": request.business_type,
|
||||
"target_audience": request.target_audience,
|
||||
"story_type": actual_story_type,
|
||||
"tone": actual_tone
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return FacebookStoryResponse(
|
||||
**self._handle_error(e, "Facebook story generation")
|
||||
)
|
||||
|
||||
def _build_story_prompt(self, request: FacebookStoryRequest, story_type: str, tone: str) -> str:
|
||||
"""
|
||||
Build the prompt for Facebook story generation.
|
||||
|
||||
Args:
|
||||
request: The story request
|
||||
story_type: The actual story type (resolved from custom if needed)
|
||||
tone: The actual tone (resolved from custom if needed)
|
||||
|
||||
Returns:
|
||||
Formatted prompt string
|
||||
"""
|
||||
base_prompt = self._build_base_prompt(
|
||||
request.business_type,
|
||||
request.target_audience,
|
||||
f"Create a {story_type} story"
|
||||
)
|
||||
|
||||
# Advanced writing flags
|
||||
advanced_lines = []
|
||||
if getattr(request, "use_hook", True):
|
||||
advanced_lines.append("- Start with a compelling hook in the first line")
|
||||
if getattr(request, "use_story", True):
|
||||
advanced_lines.append("- Use a mini narrative with a clear flow")
|
||||
if getattr(request, "use_cta", True):
|
||||
cta_text = request.visual_options.call_to_action or "Add a clear call-to-action"
|
||||
advanced_lines.append(f"- Include a CTA: {cta_text}")
|
||||
if getattr(request, "use_question", True):
|
||||
advanced_lines.append("- Ask a question to prompt replies or taps")
|
||||
if getattr(request, "use_emoji", True):
|
||||
advanced_lines.append("- Use a few relevant emojis for tone and scannability")
|
||||
if getattr(request, "use_hashtags", True):
|
||||
advanced_lines.append("- Include 1-3 relevant hashtags if appropriate")
|
||||
|
||||
advanced_str = "\n".join(advanced_lines)
|
||||
|
||||
# Visual details
|
||||
v = request.visual_options
|
||||
interactive_types_str = ", ".join(v.interactive_types) if v.interactive_types else "None specified"
|
||||
newline = '\n'
|
||||
|
||||
prompt = f"""
|
||||
{base_prompt}
|
||||
|
||||
Generate a Facebook Story with the following specifications:
|
||||
|
||||
Story Type: {story_type}
|
||||
Tone: {tone}
|
||||
|
||||
Content Requirements:
|
||||
- Include: {request.include or 'N/A'}
|
||||
- Avoid: {request.avoid or 'N/A'}
|
||||
{newline + advanced_str if advanced_str else ''}
|
||||
|
||||
Visual Options:
|
||||
- Background Type: {v.background_type}
|
||||
- Background Visual Prompt: {v.background_image_prompt or 'N/A'}
|
||||
- Gradient Style: {v.gradient_style or 'N/A'}
|
||||
- Text Overlay: {v.text_overlay}
|
||||
- Text Style: {v.text_style or 'N/A'}
|
||||
- Text Color: {v.text_color or 'N/A'}
|
||||
- Text Position: {v.text_position or 'N/A'}
|
||||
- Stickers/Emojis: {v.stickers}
|
||||
- Interactive Elements: {v.interactive_elements}
|
||||
- Interactive Types: {interactive_types_str}
|
||||
- Call To Action: {v.call_to_action or 'N/A'}
|
||||
|
||||
Please create a Facebook Story that:
|
||||
1. Is optimized for mobile viewing (vertical format)
|
||||
2. Has concise, impactful text (stories are viewed quickly)
|
||||
3. Includes clear visual direction for designers
|
||||
4. Maintains {tone} tone throughout
|
||||
5. Encourages viewer interaction
|
||||
6. Fits the {story_type} format
|
||||
7. Appeals to: {request.target_audience}
|
||||
|
||||
Format the response with:
|
||||
- Main story text/copy
|
||||
- Visual description
|
||||
- Text overlay suggestions
|
||||
- Interactive element suggestions (if enabled)
|
||||
|
||||
Keep it engaging and story-appropriate for Facebook's ephemeral format.
|
||||
"""
|
||||
|
||||
return prompt
|
||||
|
||||
def _generate_visual_suggestions(self, story_type: str, visual_options) -> List[str]:
|
||||
"""Generate visual suggestions based on story type and options."""
|
||||
suggestions = []
|
||||
|
||||
if story_type == "Product showcase":
|
||||
suggestions.extend([
|
||||
"Use high-quality product photos with clean backgrounds",
|
||||
"Include multiple angles or features in carousel format",
|
||||
"Add animated elements to highlight key features"
|
||||
])
|
||||
elif story_type == "Behind the scenes":
|
||||
suggestions.extend([
|
||||
"Use candid, authentic photos/videos",
|
||||
"Show the process or journey",
|
||||
"Include team members or workspace shots"
|
||||
])
|
||||
elif story_type == "Tutorial/How-to":
|
||||
suggestions.extend([
|
||||
"Break down steps with numbered overlays",
|
||||
"Use before/after comparisons",
|
||||
"Include clear, step-by-step visuals"
|
||||
])
|
||||
|
||||
# Add general suggestions based on visual options
|
||||
if getattr(visual_options, "text_overlay", True):
|
||||
suggestions.append("Use bold, readable fonts for text overlays")
|
||||
if getattr(visual_options, "text_style", None):
|
||||
suggestions.append(f"Match text style to tone: {visual_options.text_style}")
|
||||
if getattr(visual_options, "text_color", None):
|
||||
suggestions.append(f"Ensure sufficient contrast with text color: {visual_options.text_color}")
|
||||
if getattr(visual_options, "text_position", None):
|
||||
suggestions.append(f"Place text at {visual_options.text_position} to avoid occluding subject")
|
||||
|
||||
if getattr(visual_options, "stickers", True):
|
||||
suggestions.append("Add relevant emojis and stickers to increase engagement")
|
||||
|
||||
if getattr(visual_options, "interactive_elements", True):
|
||||
suggestions.append("Include polls, questions, or swipe-up actions")
|
||||
if getattr(visual_options, "interactive_types", None):
|
||||
suggestions.append(f"Try interactive types: {', '.join(visual_options.interactive_types)}")
|
||||
|
||||
if getattr(visual_options, "background_type", None) in {"Image", "Video"} and getattr(visual_options, "background_image_prompt", None):
|
||||
suggestions.append("Source visuals based on background prompt for consistency")
|
||||
|
||||
if getattr(visual_options, "call_to_action", None):
|
||||
suggestions.append(f"Overlay CTA copy near focal point: {visual_options.call_to_action}")
|
||||
|
||||
return suggestions
|
||||
|
||||
def _generate_engagement_tips(self, content_type: str) -> List[str]:
|
||||
"""Generate engagement tips specific to stories."""
|
||||
return [
|
||||
"Post at peak audience activity times",
|
||||
"Use interactive stickers to encourage participation",
|
||||
"Keep text minimal and highly readable",
|
||||
"Include a clear call-to-action",
|
||||
"Use trending hashtags in story text",
|
||||
"Tag relevant accounts to increase reach",
|
||||
"Save important stories as Highlights"
|
||||
]
|
||||
Reference in New Issue
Block a user