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()