Files
ALwrity/backend/api/facebook_writer/services/story_service.py

229 lines
10 KiB
Python

"""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.text_to_image_generation.gen_gemini_images import (
generate_gemini_images_base64,
)
except Exception:
generate_gemini_images_base64 = 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
# Build the prompt
prompt = self._build_story_prompt(request, actual_story_type, actual_tone)
# 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 Gemini
images_base64: List[str] = []
try:
if generate_gemini_images_base64 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."
)
images_base64 = generate_gemini_images_base64(
img_prompt,
enhance_prompt=False,
aspect_ratio="9:16",
max_retries=2,
initial_retry_delay=1.0,
) or []
except Exception:
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"
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'}
{('\n' + 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"
]