|
|
import streamlit as st |
|
|
import os |
|
|
import re |
|
|
|
|
|
|
|
|
try: |
|
|
from openai import OpenAI |
|
|
OPENAI_V1 = True |
|
|
except ImportError: |
|
|
import openai |
|
|
OPENAI_V1 = False |
|
|
|
|
|
|
|
|
def get_openai_client(): |
|
|
api_key = os.getenv("OPENAI_API_KEY") or st.secrets.get("OPENAI_API_KEY") |
|
|
if not api_key: |
|
|
st.error("OpenAI API key not found. Please set OPENAI_API_KEY in your environment variables or Streamlit secrets.") |
|
|
return None |
|
|
|
|
|
if OPENAI_V1: |
|
|
return OpenAI(api_key=api_key) |
|
|
else: |
|
|
openai.api_key = api_key |
|
|
return openai |
|
|
|
|
|
|
|
|
def analyze_subject_line(subject_line): |
|
|
"""Analyze subject line for key metrics""" |
|
|
analysis = { |
|
|
'character_count': len(subject_line), |
|
|
'word_count': len(subject_line.split()), |
|
|
'mobile_preview': subject_line[:33] + "..." if len(subject_line) > 33 else subject_line, |
|
|
'has_emoji': bool(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\U0001F1E0-\U0001F1FF\U00002600-\U000027BF]', subject_line)), |
|
|
'has_numbers': bool(re.search(r'\d', subject_line)), |
|
|
'exclamation_count': subject_line.count('!'), |
|
|
} |
|
|
|
|
|
|
|
|
score = 8 |
|
|
|
|
|
|
|
|
if analysis['word_count'] <= 4: |
|
|
score += 1 |
|
|
elif analysis['word_count'] > 7: |
|
|
score -= 1 |
|
|
|
|
|
if analysis['character_count'] <= 33: |
|
|
score += 1 |
|
|
elif analysis['character_count'] > 50: |
|
|
score -= 2 |
|
|
|
|
|
|
|
|
if analysis['has_numbers']: |
|
|
score += 1 |
|
|
if analysis['has_emoji']: |
|
|
score += 1 |
|
|
|
|
|
|
|
|
if analysis['exclamation_count'] > 1: |
|
|
score -= 2 |
|
|
|
|
|
analysis['score'] = max(1, min(10, score)) |
|
|
return analysis |
|
|
|
|
|
def generate_subject_lines_advanced(audience, message, tone, psychology_framework, industry, personalization, client): |
|
|
"""Generate high-quality, compelling subject lines""" |
|
|
try: |
|
|
framework_strategies = { |
|
|
"FOMO (Fear of Missing Out)": "Create genuine urgency and scarcity. Use time-sensitive language, countdown elements, or limited availability", |
|
|
"Curiosity Gap": "Create compelling information gaps that demand closure. Hint at valuable secrets, surprising revelations, or counterintuitive insights", |
|
|
"Social Proof": "Leverage what others are doing - specific user numbers, testimonials, trending status, or peer behavior", |
|
|
"Loss Aversion": "Frame what they're currently missing or losing rather than what they could gain", |
|
|
"Reciprocity": "Lead with valuable gifts, free resources, or helpful insights before asking for anything", |
|
|
"Authority": "Reference credible experts, research data, proven methodologies, or industry leadership", |
|
|
"Scarcity": "Emphasize genuine limited availability, exclusive access, or member-only benefits" |
|
|
} |
|
|
|
|
|
framework_strategy = framework_strategies.get(psychology_framework, '') |
|
|
|
|
|
prompt_text = f"""You are a world-class email copywriter who has written subject lines for the highest-performing email campaigns. Your subject lines are known for being irresistibly compelling while staying authentic. |
|
|
|
|
|
Campaign Context: |
|
|
- Target Audience: {audience} |
|
|
- Core Message: {message} |
|
|
- Brand Tone: {tone} |
|
|
- Psychological Strategy: {psychology_framework} |
|
|
- Industry Context: {industry} |
|
|
- Personalization Approach: {personalization} |
|
|
|
|
|
Advanced Strategy: {framework_strategy} |
|
|
|
|
|
Your Mission: Create 10 subject lines that would make someone instantly stop scrolling and click. Each should feel fresh, compelling, and impossible to ignore. |
|
|
|
|
|
CRITICAL EMOJI REQUIREMENT: Include emojis in at least 6 out of 10 subject lines (60%+). Emojis increase open rates by 39% when used appropriately. Place them strategically at the beginning or end for maximum impact. |
|
|
|
|
|
LENGTH VARIATION REQUIREMENT: |
|
|
- 7 subject lines: Ultra-short (2-4 words, max 33 characters) for mobile optimization |
|
|
- 2 subject lines: Medium length (5-7 words, 34-50 characters) for desktop impact |
|
|
- 1 subject line: Longer format (8+ words, 51-65 characters) for storytelling appeal |
|
|
|
|
|
Quality Requirements: |
|
|
1. Apply {psychology_framework} psychology masterfully, not obviously |
|
|
2. Include power words that trigger emotional response |
|
|
3. Use numbers strategically (proven to increase opens by 45%) |
|
|
4. Make each line feel unique and scroll-stopping |
|
|
5. Avoid clichés, generic phrases, or obvious sales language |
|
|
6. Ensure emojis enhance rather than distract from the message |
|
|
|
|
|
Examples of compelling techniques: |
|
|
- Specific numbers: "2-min trick", "73% faster", "$847 saved" |
|
|
- Intriguing questions: "Still doing this?", "Why X?", "Ready for Y?" |
|
|
- Surprising statements: "We're wrong", "This failed", "Oops..." |
|
|
- Personal urgency: "Your deadline", "Behind schedule?", "Missing this?" |
|
|
|
|
|
CRITICAL: Output format must be exactly: |
|
|
**1.** [subject line] (Length: Short/Medium/Long) |
|
|
[preheader text] |
|
|
|
|
|
**2.** [subject line] (Length: Short/Medium/Long) |
|
|
[preheader text] |
|
|
|
|
|
**3.** [subject line] (Length: Short/Medium/Long) |
|
|
[preheader text] |
|
|
|
|
|
Continue for all 10 options. Make each subject line feel like it was crafted by a master copywriter, not an AI tool. Remember: 6+ must have emojis, vary the lengths as specified.""" |
|
|
|
|
|
if OPENAI_V1: |
|
|
response = client.chat.completions.create( |
|
|
model="gpt-4o", |
|
|
messages=[{"role": "user", "content": prompt_text}], |
|
|
max_tokens=1000, |
|
|
temperature=0.7 |
|
|
) |
|
|
return response.choices[0].message.content |
|
|
else: |
|
|
response = client.ChatCompletion.create( |
|
|
model="gpt-4o", |
|
|
messages=[{"role": "user", "content": prompt_text}], |
|
|
max_tokens=1000, |
|
|
temperature=0.7 |
|
|
) |
|
|
return response.choices[0].message.content |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Error generating subject lines: {str(e)}") |
|
|
return None |
|
|
|
|
|
def optimize_existing_subject_line_clean(existing_subject, existing_preheader, audience, message, tone, psychology_framework, industry, personalization, client): |
|
|
"""Advanced optimization with superior subject lines""" |
|
|
try: |
|
|
analysis = analyze_subject_line(existing_subject) |
|
|
preheader_text = f"Current preheader: {existing_preheader}" if existing_preheader else "No preheader provided" |
|
|
|
|
|
prompt_text = f"""You are an elite email optimization specialist who transforms mediocre subject lines into irresistible ones. Your optimizations consistently achieve 40%+ higher open rates. |
|
|
|
|
|
CURRENT SUBJECT LINE ANALYSIS: |
|
|
"{existing_subject}" |
|
|
{preheader_text} |
|
|
Length: {analysis['character_count']} characters, {analysis['word_count']} words |
|
|
Mobile Preview: "{analysis['mobile_preview']}" |
|
|
Current Effectiveness Score: {analysis['score']}/10 |
|
|
|
|
|
OPTIMIZATION BRIEF: |
|
|
- Target Audience: {audience} |
|
|
- Core Message: {message} |
|
|
- Desired Tone: {tone} |
|
|
- Psychology to Apply: {psychology_framework} |
|
|
- Industry Context: {industry} |
|
|
- Personalization Strategy: {personalization} |
|
|
|
|
|
YOUR MISSION: Transform this into 5 compelling alternatives that would dramatically outperform the original. |
|
|
|
|
|
CRITICAL EMOJI REQUIREMENT: Include emojis in at least 3 out of 5 optimized options (60%+). Emojis increase open rates by 39% when used strategically. |
|
|
|
|
|
LENGTH VARIATION REQUIREMENT: |
|
|
- 3 options: Ultra-short (2-4 words, max 33 characters) for mobile optimization |
|
|
- 1 option: Medium length (5-7 words, 34-50 characters) for desktop impact |
|
|
- 1 option: Longer format (8+ words, 51-65 characters) for storytelling appeal |
|
|
|
|
|
Advanced Optimization Strategies: |
|
|
1. Apply {psychology_framework} psychology masterfully |
|
|
2. Maximize mobile impact while offering variety |
|
|
3. Use power words that trigger immediate action |
|
|
4. Include specific numbers or percentages when relevant |
|
|
5. Create genuine intrigue without being misleading |
|
|
6. Leverage industry-specific pain points and desires |
|
|
7. Make each option feel scroll-stopping and urgent |
|
|
|
|
|
Quality Standards: |
|
|
- Each subject line should feel like it was written by the world's best copywriter |
|
|
- Avoid generic, cliché, or obviously promotional language |
|
|
- Create genuine curiosity and emotional pull |
|
|
- Ensure mobile-first optimization with strategic longer options |
|
|
- Focus on what makes people instantly want to click |
|
|
|
|
|
EXACT OUTPUT FORMAT: |
|
|
**Current:** {existing_subject} (Score: {analysis['score']}/10) |
|
|
|
|
|
**Option 1:** [compelling subject line] (Length: Short/Medium/Long) |
|
|
[irresistible preheader] |
|
|
|
|
|
**Option 2:** [compelling subject line] (Length: Short/Medium/Long) |
|
|
[irresistible preheader] |
|
|
|
|
|
**Option 3:** [compelling subject line] (Length: Short/Medium/Long) |
|
|
[irresistible preheader] |
|
|
|
|
|
**Option 4:** [compelling subject line] (Length: Short/Medium/Long) |
|
|
[irresistible preheader] |
|
|
|
|
|
**Option 5:** [compelling subject line] (Length: Short/Medium/Long) |
|
|
[irresistible preheader] |
|
|
|
|
|
Make each option significantly more compelling than the original. Remember: 3+ must have emojis, vary lengths as specified.""" |
|
|
|
|
|
if OPENAI_V1: |
|
|
response = client.chat.completions.create( |
|
|
model="gpt-4o", |
|
|
messages=[{"role": "user", "content": prompt_text}], |
|
|
max_tokens=1200, |
|
|
temperature=0.7 |
|
|
) |
|
|
return response.choices[0].message.content |
|
|
else: |
|
|
response = client.ChatCompletion.create( |
|
|
model="gpt-4o", |
|
|
messages=[{"role": "user", "content": prompt_text}], |
|
|
max_tokens=1200, |
|
|
temperature=0.7 |
|
|
) |
|
|
return response.choices[0].message.content |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Error optimizing subject line: {str(e)}") |
|
|
return None |
|
|
|
|
|
def main(): |
|
|
st.set_page_config(page_title="Email Subject Line Optimizer", layout="wide") |
|
|
|
|
|
|
|
|
st.markdown(""" |
|
|
<style> |
|
|
.main-header { text-align: center; margin-bottom: 2rem; } |
|
|
.score-box { |
|
|
padding: 8px 12px; |
|
|
border-radius: 6px; |
|
|
text-align: center; |
|
|
font-weight: bold; |
|
|
margin: 8px 0; |
|
|
} |
|
|
.score-good { background-color: #d4edda; color: #155724; } |
|
|
.score-okay { background-color: #fff3cd; color: #856404; } |
|
|
.score-poor { background-color: #f8d7da; color: #721c24; } |
|
|
.preview-mobile { |
|
|
background: #f8f9fa; |
|
|
padding: 10px; |
|
|
border-radius: 4px; |
|
|
font-family: monospace; |
|
|
border-left: 3px solid #007bff; |
|
|
} |
|
|
.sidebar .stSelectbox label { font-size: 14px !important; } |
|
|
.stExpander { margin: 0.5rem 0; } |
|
|
</style> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.markdown('<div class="main-header">', unsafe_allow_html=True) |
|
|
st.markdown("# 🚀 Email Subject Line Optimizer") |
|
|
st.markdown("*Generate high-converting subject lines using psychological frameworks*") |
|
|
st.markdown('</div>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
client = get_openai_client() |
|
|
if not client: |
|
|
return |
|
|
|
|
|
|
|
|
mode = st.radio( |
|
|
"Choose your mode:", |
|
|
["Generate New Subject Lines", "Optimize Existing Subject Line"], |
|
|
horizontal=True |
|
|
) |
|
|
|
|
|
st.markdown("---") |
|
|
|
|
|
|
|
|
col1, col2 = st.columns([2, 1]) |
|
|
|
|
|
with col1: |
|
|
|
|
|
audience_desc = st.text_input( |
|
|
"Target audience:", |
|
|
placeholder="e.g., busy entrepreneurs, working mothers, SaaS buyers" |
|
|
) |
|
|
|
|
|
message_content = st.text_area( |
|
|
"Main message or offer:", |
|
|
placeholder="e.g., 50% off sale, free webinar, product launch", |
|
|
height=80 |
|
|
) |
|
|
|
|
|
|
|
|
fcol1, fcol2 = st.columns(2) |
|
|
with fcol1: |
|
|
tone = st.selectbox( |
|
|
"Tone:", |
|
|
['Friendly', 'Professional', 'Urgent', 'Curious', 'Playful', 'Authoritative'] |
|
|
) |
|
|
|
|
|
psychology_framework = st.selectbox( |
|
|
"Psychology:", |
|
|
['Curiosity Gap', 'FOMO (Fear of Missing Out)', 'Social Proof', 'Scarcity', 'Reciprocity', 'Authority', 'Loss Aversion'] |
|
|
) |
|
|
|
|
|
with fcol2: |
|
|
industry = st.selectbox( |
|
|
"Industry:", |
|
|
['E-commerce', 'SaaS/Tech', 'B2B Services', 'Healthcare', 'Education', 'Finance', 'Marketing', 'Other'] |
|
|
) |
|
|
|
|
|
personalization = st.selectbox( |
|
|
"Personalization:", |
|
|
['First Name', 'Location', 'Purchase History', 'Behavior', 'Industry', 'Seasonal', 'None'] |
|
|
) |
|
|
|
|
|
with col2: |
|
|
|
|
|
with st.expander("📱 Quick Tips", expanded=False): |
|
|
st.markdown(""" |
|
|
**Mobile First:** 33 chars max |
|
|
**Optimal:** 2-4 words |
|
|
**Numbers:** +45% opens |
|
|
**Emojis:** +39% (use in 60%+ of options) |
|
|
**Personalization:** +26% opens |
|
|
**Length Variety:** Mix short & medium options |
|
|
""") |
|
|
|
|
|
with st.expander("🧠 Psychology Guide", expanded=False): |
|
|
st.markdown(""" |
|
|
**Curiosity:** Leave gaps, ask questions |
|
|
**FOMO:** "Last chance", "Limited time" |
|
|
**Social Proof:** "Join 1000+ users" |
|
|
**Scarcity:** "Only 3 left" |
|
|
**Reciprocity:** Free value first |
|
|
**Authority:** Expert credibility |
|
|
""") |
|
|
|
|
|
|
|
|
if mode == "Optimize Existing Subject Line": |
|
|
st.markdown("### Current Subject Line") |
|
|
|
|
|
ocol1, ocol2 = st.columns([2, 1]) |
|
|
|
|
|
with ocol1: |
|
|
existing_subject = st.text_input( |
|
|
"Subject line to optimize:", |
|
|
placeholder="Enter your current subject line" |
|
|
) |
|
|
|
|
|
existing_preheader = st.text_input( |
|
|
"Current preheader (optional):", |
|
|
placeholder="Preview text that appears after subject" |
|
|
) |
|
|
|
|
|
with ocol2: |
|
|
if existing_subject: |
|
|
analysis = analyze_subject_line(existing_subject) |
|
|
score_class = "good" if analysis['score'] >= 7 else "okay" if analysis['score'] >= 5 else "poor" |
|
|
|
|
|
st.markdown(f""" |
|
|
<div class="score-box score-{score_class}"> |
|
|
Score: {analysis['score']}/10 |
|
|
</div> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(f""" |
|
|
<div class="preview-mobile"> |
|
|
<strong>Mobile:</strong> {analysis['mobile_preview']} |
|
|
</div> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.metric("Length", f"{analysis['character_count']} chars") |
|
|
st.metric("Words", analysis['word_count']) |
|
|
|
|
|
generate_button = st.button('🔧 Optimize Subject Line', use_container_width=True, type="primary") |
|
|
|
|
|
if generate_button: |
|
|
if not existing_subject.strip(): |
|
|
st.error("Please enter a subject line to optimize.") |
|
|
return |
|
|
|
|
|
if not audience_desc.strip() or not message_content.strip(): |
|
|
st.error("Please provide audience and message details.") |
|
|
return |
|
|
|
|
|
with st.spinner("Optimizing your subject line..."): |
|
|
response = optimize_existing_subject_line_clean( |
|
|
existing_subject, existing_preheader, audience_desc, message_content, |
|
|
tone, psychology_framework, industry, personalization, client |
|
|
) |
|
|
|
|
|
if response: |
|
|
st.markdown("### Optimized Results") |
|
|
st.markdown(response) |
|
|
|
|
|
else: |
|
|
generate_button = st.button('✨ Generate Subject Lines', use_container_width=True, type="primary") |
|
|
|
|
|
if generate_button: |
|
|
if not audience_desc.strip() or not message_content.strip(): |
|
|
st.error("Please provide audience and message details.") |
|
|
return |
|
|
|
|
|
with st.spinner("Generating optimized subject lines..."): |
|
|
response = generate_subject_lines_advanced( |
|
|
audience_desc, message_content, tone, psychology_framework, |
|
|
industry, personalization, client |
|
|
) |
|
|
|
|
|
if response: |
|
|
st.markdown("### Your Optimized Subject Lines") |
|
|
st.markdown(response) |
|
|
|
|
|
|
|
|
st.markdown("---") |
|
|
with st.expander("📊 Why These Practices Work", expanded=False): |
|
|
st.markdown(""" |
|
|
**Research-backed optimization:** |
|
|
- 64% of people decide to open based on subject line alone |
|
|
- Mobile users see only 33 characters before truncation |
|
|
- Subject lines with numbers get 45% higher open rates |
|
|
- Personalized subject lines are 26% more likely to be opened |
|
|
- Emojis can increase open rates by 39% when used appropriately |
|
|
- 2-4 word subject lines achieve the highest engagement |
|
|
- Length variety provides options for different audiences and contexts |
|
|
""") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |