File size: 17,619 Bytes
cac9eab 0e8d5d9 b2c9e15 24b72ba a77f157 0e8d5d9 a77f157 0e8d5d9 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 0e8d5d9 137a954 0648774 b2c9e15 0648774 b2c9e15 a4ce1f3 0648774 0e8d5d9 0648774 b2c9e15 a4ce1f3 b2c9e15 0648774 b2c9e15 e91311e b2c9e15 0648774 a4ce1f3 0648774 a4ce1f3 0648774 e91311e 0648774 e91311e 0648774 e91311e 0648774 e91311e 0e8d5d9 a77f157 e91311e a77f157 e91311e a77f157 0e8d5d9 137a954 0e8d5d9 137a954 0648774 1c8ff11 b2c9e15 0e8d5d9 0648774 0e8d5d9 0648774 0e8d5d9 0648774 0e8d5d9 e91311e 0648774 e91311e 0648774 0e8d5d9 0648774 e91311e 0648774 b2c9e15 0648774 0e8d5d9 e91311e 0648774 0e8d5d9 e91311e 0648774 b2c9e15 e91311e 0648774 b2c9e15 e91311e 0648774 0e8d5d9 e91311e 0648774 0e8d5d9 e91311e 0e8d5d9 a77f157 e91311e a77f157 e91311e a77f157 1c8ff11 137a954 5ebf920 862456a 0e8d5d9 137a954 0e8d5d9 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 0e8d5d9 137a954 0e8d5d9 137a954 b2c9e15 0e8d5d9 b2c9e15 0e8d5d9 b2c9e15 137a954 0e8d5d9 b2c9e15 137a954 0e8d5d9 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 0e8d5d9 137a954 e91311e 137a954 b2c9e15 137a954 0e8d5d9 137a954 0e8d5d9 137a954 0e8d5d9 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 b2c9e15 137a954 0e8d5d9 137a954 0e8d5d9 137a954 0e8d5d9 137a954 b2c9e15 0e8d5d9 137a954 0e8d5d9 137a954 0e8d5d9 137a954 0e8d5d9 137a954 b2c9e15 0e8d5d9 137a954 0e8d5d9 137a954 0e8d5d9 137a954 b2c9e15 137a954 e91311e 0e8d5d9 1813aa1 0e8d5d9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
import streamlit as st
import os
import re
# Handle both old and new OpenAI library versions
try:
from openai import OpenAI
OPENAI_V1 = True
except ImportError:
import openai
OPENAI_V1 = False
# Initialize OpenAI client
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
# Subject line analysis functions
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('!'),
}
# Simple scoring
score = 8 # Start with good score
# Length optimization (2-4 words ideal, 33 chars max for mobile)
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
# Bonuses
if analysis['has_numbers']:
score += 1
if analysis['has_emoji']:
score += 1
# Penalties
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")
# Clean, minimal styling
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)
# Header
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)
# Initialize OpenAI client
client = get_openai_client()
if not client:
return
# Mode selection
mode = st.radio(
"Choose your mode:",
["Generate New Subject Lines", "Optimize Existing Subject Line"],
horizontal=True
)
st.markdown("---")
# Main content in columns
col1, col2 = st.columns([2, 1])
with col1:
# Basic inputs
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
)
# Framework selection in columns
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:
# Compact tips section
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
""")
# Mode-specific sections
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 New Subject Lines
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)
# Simple footer
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() |