Toshi94 commited on
Commit
7f8c46f
Β·
verified Β·
1 Parent(s): bf27019

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1288 -0
app.py ADDED
@@ -0,0 +1,1288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import re
4
+ from datetime import datetime, timedelta
5
+ import json
6
+ import random
7
+ from google.colab import userdata
8
+
9
+ # Import LangChain components
10
+ try:
11
+ from langchain_groq import ChatGroq
12
+ from langchain_core.prompts import PromptTemplate
13
+ from langchain_core.output_parsers import StrOutputParser
14
+ print("βœ… All packages imported successfully!")
15
+ except ImportError as e:
16
+ print(f"❌ Import Error: {e}")
17
+ print("\n⚠️ Please run the installation command first:")
18
+ print("!pip install --upgrade langchain langchain-core langchain-community langchain-groq gradio python-dotenv")
19
+ raise
20
+
21
+ # =========================
22
+ # API Key Configuration
23
+ # =========================
24
+ #GROQ_API_KEY = "add your api key or load from google secerets"
25
+
26
+ GROQ_API_KEY = userdata.get('GROQ_API_KEY')
27
+
28
+
29
+ # =========================
30
+ # Initialize LLM
31
+ # =========================
32
+ try:
33
+ llm = ChatGroq(
34
+ temperature=0.7,
35
+ groq_api_key=GROQ_API_KEY,
36
+ model_name="llama-3.1-8b-instant"
37
+ )
38
+ print("βœ… LLM initialized successfully!")
39
+ except Exception as e:
40
+ print(f"❌ LLM initialization error: {e}")
41
+ raise
42
+
43
+ # =========================
44
+ # Enhanced Prompt Templates
45
+ # =========================
46
+ linkedin_template = """
47
+ You are an expert LinkedIn content writer with 10+ years of experience.
48
+ Create a highly engaging LinkedIn post that maximizes engagement.
49
+
50
+ Requirements:
51
+ - Start with a powerful hook that stops scrolling
52
+ - Use short, punchy paragraphs (2-3 lines max)
53
+ - Include storytelling elements or data points
54
+ - Add 2-4 relevant emojis naturally throughout
55
+ - Use line breaks for visual appeal
56
+ - End with an engaging CTA
57
+ - Word count: {word_count} words
58
+
59
+ Topic: {topic}
60
+ Tone: {tone}
61
+ Target Audience: {audience}
62
+ Post Type: {post_type}
63
+ {hashtags_instruction}
64
+
65
+ Write the post now with natural emoji placement.
66
+ """
67
+
68
+ carousel_template = """
69
+ Create a LinkedIn carousel post with {slides} slides.
70
+ Each slide should have:
71
+ - A catchy title (5-8 words)
72
+ - 2-3 bullet points of content
73
+ - Relevant emoji
74
+
75
+ Topic: {topic}
76
+ Tone: {tone}
77
+
78
+ Format each slide as:
79
+ SLIDE [number]:
80
+ Title: [title]
81
+ β€’ [point 1]
82
+ β€’ [point 2]
83
+ β€’ [point 3]
84
+ """
85
+
86
+ story_template = """
87
+ Create a compelling LinkedIn story post about {topic}.
88
+ Use the following structure:
89
+ 1. Opening hook (2-3 lines)
90
+ 2. The challenge/situation
91
+ 3. The turning point
92
+ 4. The lesson/insight
93
+ 5. Call to action
94
+
95
+ Tone: {tone}
96
+ Include emojis naturally.
97
+ Target length: {word_count} words
98
+ """
99
+
100
+ thread_template = """
101
+ Create a LinkedIn thread with {posts} posts on {topic}.
102
+ Each post should:
103
+ - Be standalone valuable
104
+ - Connect to the overall narrative
105
+ - Be 100-150 words
106
+ - Include relevant emojis
107
+
108
+ Tone: {tone}
109
+ Format as:
110
+ POST 1/[total]:
111
+ [content]
112
+
113
+ POST 2/[total]:
114
+ [content]
115
+ """
116
+
117
+ # =========================
118
+ # Post History Storage
119
+ # =========================
120
+ post_history = []
121
+
122
+ def save_to_history(post, topic, tone, audience):
123
+ """Save generated post to history"""
124
+ entry = {
125
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
126
+ "post": post,
127
+ "topic": topic,
128
+ "tone": tone,
129
+ "audience": audience
130
+ }
131
+ post_history.insert(0, entry)
132
+ if len(post_history) > 50:
133
+ post_history.pop()
134
+ return format_history()
135
+
136
+ def format_history():
137
+ """Format history for display"""
138
+ if not post_history:
139
+ return "No posts in history yet. Generate some posts to see them here!"
140
+
141
+ formatted = []
142
+ for idx, entry in enumerate(post_history[:10], 1):
143
+ formatted.append(f"""
144
+ **Post #{idx}** - {entry['timestamp']}
145
+ πŸ“ Topic: {entry['topic']}
146
+ 🎭 Tone: {entry['tone']} | 🎯 Audience: {entry['audience']}
147
+
148
+ {entry['post'][:200]}...
149
+
150
+ ---
151
+ """)
152
+ return "\n".join(formatted)
153
+
154
+ def get_post_from_history(post_number):
155
+ """Retrieve specific post from history"""
156
+ try:
157
+ idx = int(post_number) - 1
158
+ if 0 <= idx < len(post_history):
159
+ return post_history[idx]['post']
160
+ return "Invalid post number"
161
+ except:
162
+ return "Please enter a valid post number"
163
+
164
+ # =========================
165
+ # Schedule Optimizer
166
+ # =========================
167
+ def get_best_posting_times(industry, audience_location, goal):
168
+ """AI-powered posting time recommendations"""
169
+ template = """
170
+ You are a LinkedIn marketing expert specializing in optimal posting times.
171
+
172
+ Analyze and recommend the best 5 posting times for:
173
+ Industry: {industry}
174
+ Audience Location: {audience_location}
175
+ Goal: {goal}
176
+
177
+ Provide specific day and time recommendations with reasoning.
178
+ Format as:
179
+ 1. [Day] at [Time] - [Reason]
180
+ 2. [Day] at [Time] - [Reason]
181
+ ...
182
+
183
+ Also include:
184
+ - Best day of week overall
185
+ - Times to avoid
186
+ - Frequency recommendation
187
+ """
188
+
189
+ prompt = PromptTemplate(
190
+ input_variables=["industry", "audience_location", "goal"],
191
+ template=template
192
+ )
193
+ chain = prompt | llm | StrOutputParser()
194
+
195
+ result = chain.invoke({
196
+ "industry": industry,
197
+ "audience_location": audience_location,
198
+ "goal": goal
199
+ })
200
+
201
+ return result
202
+
203
+ # =========================
204
+ # Competitor Analysis
205
+ # =========================
206
+ def analyze_competitor_post(competitor_post, your_niche):
207
+ """Analyze what makes a post successful"""
208
+ template = """
209
+ You are a LinkedIn growth expert. Analyze this successful post and extract insights.
210
+
211
+ Niche/Industry: {niche}
212
+
213
+ Post to analyze:
214
+ {post}
215
+
216
+ Provide detailed analysis:
217
+ 1. **Hook Analysis**: Why the opening works
218
+ 2. **Structure**: How it's organized
219
+ 3. **Engagement Tactics**: What drives comments/shares
220
+ 4. **Key Elements**: Specific techniques used
221
+ 5. **Actionable Tips**: How to replicate success
222
+ 6. **Improvement Ideas**: What could make it even better
223
+
224
+ Be specific and tactical.
225
+ """
226
+
227
+ prompt = PromptTemplate(
228
+ input_variables=["niche", "post"],
229
+ template=template
230
+ )
231
+ chain = prompt | llm | StrOutputParser()
232
+
233
+ result = chain.invoke({
234
+ "niche": your_niche,
235
+ "post": competitor_post
236
+ })
237
+
238
+ return result
239
+
240
+ # =========================
241
+ # Image Suggestion
242
+ # =========================
243
+ def suggest_images(post_content, post_type):
244
+ """AI suggests what images/visuals to use"""
245
+ template = """
246
+ You are a visual content strategist for LinkedIn.
247
+
248
+ Analyze this post and suggest the best visual content to accompany it.
249
+
250
+ Post Type: {post_type}
251
+ Post Content:
252
+ {content}
253
+
254
+ Suggest:
255
+ 1. **Primary Image Type**: (e.g., infographic, photo, illustration, chart)
256
+ 2. **Specific Visual Elements**: What should be shown
257
+ 3. **Color Scheme**: Recommended colors
258
+ 4. **Text Overlay**: What text (if any) should be on the image
259
+ 5. **Style Guidelines**: Professional, casual, modern, etc.
260
+ 6. **Alternative Options**: 2-3 other visual ideas
261
+ 7. **Stock Photo Keywords**: Keywords to search for the right image
262
+ 8. **Design Tools**: Recommended tools (Canva templates, etc.)
263
+
264
+ Be specific and actionable.
265
+ """
266
+
267
+ prompt = PromptTemplate(
268
+ input_variables=["post_type", "content"],
269
+ template=template
270
+ )
271
+ chain = prompt | llm | StrOutputParser()
272
+
273
+ result = chain.invoke({
274
+ "post_type": post_type,
275
+ "content": post_content
276
+ })
277
+
278
+ return result
279
+
280
+ # =========================
281
+ # A/B Testing
282
+ # =========================
283
+ def generate_ab_versions(topic, tone, audience):
284
+ """Generate two different versions for A/B testing"""
285
+ template_a = """
286
+ Create Version A - Hook-focused approach:
287
+
288
+ Topic: {topic}
289
+ Tone: {tone}
290
+ Audience: {audience}
291
+
292
+ Start with an extremely powerful, curiosity-driven hook.
293
+ Focus on immediate attention-grabbing.
294
+ Use pattern interrupts.
295
+ 150-200 words.
296
+ """
297
+
298
+ template_b = """
299
+ Create Version B - Value-first approach:
300
+
301
+ Topic: {topic}
302
+ Tone: {tone}
303
+ Audience: {audience}
304
+
305
+ Start by immediately delivering value/insight.
306
+ Focus on practical takeaways.
307
+ Use data or specific examples.
308
+ 150-200 words.
309
+ """
310
+
311
+ prompt_a = PromptTemplate(input_variables=["topic", "tone", "audience"], template=template_a)
312
+ prompt_b = PromptTemplate(input_variables=["topic", "tone", "audience"], template=template_b)
313
+
314
+ chain_a = prompt_a | llm | StrOutputParser()
315
+ chain_b = prompt_b | llm | StrOutputParser()
316
+
317
+ version_a = chain_a.invoke({"topic": topic, "tone": tone, "audience": audience})
318
+ version_b = chain_b.invoke({"topic": topic, "tone": tone, "audience": audience})
319
+
320
+ version_a = fix_emoji_encoding(version_a)
321
+ version_b = fix_emoji_encoding(version_b)
322
+
323
+ analytics_a = analyze_post(version_a)
324
+ analytics_b = analyze_post(version_b)
325
+
326
+ comparison = f"""
327
+ # πŸ…°οΈ VERSION A - Hook-Focused
328
+ **Strategy**: Attention-grabbing hook, curiosity-driven
329
+
330
+ {version_a}
331
+
332
+ {format_analytics(analytics_a)}
333
+
334
+ ---
335
+
336
+ # πŸ…±οΈ VERSION B - Value-First
337
+ **Strategy**: Immediate value delivery, practical focus
338
+
339
+ {version_b}
340
+
341
+ {format_analytics(analytics_b)}
342
+
343
+ ---
344
+
345
+ ## πŸ“Š A/B Testing Recommendations:
346
+ - Test both versions at similar times (Tuesday/Wednesday morning)
347
+ - Track: Impressions, Engagement Rate, Comments, Shares
348
+ - Run each for 24-48 hours
349
+ - The version with higher engagement rate (not just likes) wins
350
+ - Consider your audience: C-level prefers Version B, broader audience may prefer Version A
351
+ """
352
+
353
+ return comparison
354
+
355
+ # =========================
356
+ # Trend Analyzer
357
+ # =========================
358
+ def analyze_linkedin_trends(industry, timeframe):
359
+ """Get current LinkedIn trending topics"""
360
+ template = """
361
+ You are a LinkedIn trends analyst with access to current platform data.
362
+
363
+ Analyze current LinkedIn trends for:
364
+ Industry: {industry}
365
+ Timeframe: {timeframe}
366
+
367
+ Provide:
368
+ 1. **Top 5 Trending Topics**: What's getting traction now
369
+ 2. **Rising Hashtags**: Trending hashtags to use
370
+ 3. **Content Formats**: Which formats are performing best (text, carousel, video, etc.)
371
+ 4. **Engagement Patterns**: What drives engagement now
372
+ 5. **Topic Ideas**: 5 specific post ideas based on trends
373
+ 6. **What to Avoid**: Topics that are oversaturated
374
+
375
+ Be current, specific, and actionable.
376
+ """
377
+
378
+ prompt = PromptTemplate(
379
+ input_variables=["industry", "timeframe"],
380
+ template=template
381
+ )
382
+ chain = prompt | llm | StrOutputParser()
383
+
384
+ result = chain.invoke({
385
+ "industry": industry,
386
+ "timeframe": timeframe
387
+ })
388
+
389
+ return result
390
+
391
+ # =========================
392
+ # Personal Branding
393
+ # =========================
394
+ def create_brand_voice(name, industry, values, personality, expertise):
395
+ """Create a consistent personal brand voice guide"""
396
+ template = """
397
+ You are a personal branding expert. Create a comprehensive brand voice guide.
398
+
399
+ Profile:
400
+ - Name: {name}
401
+ - Industry: {industry}
402
+ - Core Values: {values}
403
+ - Personality: {personality}
404
+ - Key Expertise: {expertise}
405
+
406
+ Create a detailed brand voice guide including:
407
+
408
+ 1. **Voice Characteristics**: 3-5 key traits
409
+ 2. **Tone Guidelines**:
410
+ - Professional contexts
411
+ - Casual contexts
412
+ - Thought leadership
413
+ 3. **Language Do's and Don'ts**:
414
+ - Preferred words/phrases
415
+ - Words to avoid
416
+ 4. **Signature Elements**:
417
+ - Opening styles
418
+ - Closing CTAs
419
+ - Emoji usage
420
+ 5. **Content Pillars**: 5 main topic areas
421
+ 6. **Example Phrases**: 10 on-brand phrases to use
422
+ 7. **Differentiation**: What makes this voice unique
423
+
424
+ Be specific and actionable for consistent LinkedIn presence.
425
+ """
426
+
427
+ prompt = PromptTemplate(
428
+ input_variables=["name", "industry", "values", "personality", "expertise"],
429
+ template=template
430
+ )
431
+ chain = prompt | llm | StrOutputParser()
432
+
433
+ result = chain.invoke({
434
+ "name": name,
435
+ "industry": industry,
436
+ "values": values,
437
+ "personality": personality,
438
+ "expertise": expertise
439
+ })
440
+
441
+ return result
442
+
443
+ def apply_brand_voice(post, brand_guide_summary):
444
+ """Apply brand voice to a post"""
445
+ template = """
446
+ Rewrite this post to match the brand voice guidelines.
447
+
448
+ Brand Voice Guidelines:
449
+ {brand_guide}
450
+
451
+ Original Post:
452
+ {post}
453
+
454
+ Rewrite maintaining the core message but adapting tone, language, and style to match the brand voice perfectly.
455
+ """
456
+
457
+ prompt = PromptTemplate(
458
+ input_variables=["brand_guide", "post"],
459
+ template=template
460
+ )
461
+ chain = prompt | llm | StrOutputParser()
462
+
463
+ result = chain.invoke({
464
+ "brand_guide": brand_guide_summary,
465
+ "post": post
466
+ })
467
+
468
+ return fix_emoji_encoding(result)
469
+
470
+ # =========================
471
+ # Export Functions - FIXED
472
+ # =========================
473
+ def export_as_text(content, filename="linkedin_post"):
474
+ """Export post as downloadable text file"""
475
+ if not content or not content.strip():
476
+ return None
477
+
478
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
479
+ full_filename = f"{filename}_{timestamp}.txt"
480
+
481
+ # Create file in current directory for proper download
482
+ try:
483
+ with open(full_filename, 'w', encoding='utf-8') as f:
484
+ f.write(content)
485
+ return full_filename
486
+ except Exception as e:
487
+ print(f"Export error: {e}")
488
+ return None
489
+
490
+ def create_post_document(posts_list):
491
+ """Create a formatted document with multiple posts"""
492
+ if not posts_list:
493
+ return ""
494
+
495
+ doc_content = f"""
496
+ LinkedIn Content Package
497
+ Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
498
+ {'='*80}
499
+
500
+ """
501
+ for idx, post in enumerate(posts_list, 1):
502
+ doc_content += f"""
503
+ POST #{idx}
504
+ {'-'*80}
505
+ {post}
506
+
507
+ {'='*80}
508
+
509
+ """
510
+ return doc_content
511
+
512
+ # =========================
513
+ # Helper Functions
514
+ # =========================
515
+ def suggest_tone(topic):
516
+ topic_lower = topic.lower()
517
+ if any(word in topic_lower for word in ["success", "growth", "motivation", "inspire"]):
518
+ return "Inspirational"
519
+ elif any(word in topic_lower for word in ["team", "colleague", "work", "project", "career"]):
520
+ return "Professional"
521
+ elif any(word in topic_lower for word in ["data", "research", "analysis", "tech"]):
522
+ return "Analytical"
523
+ else:
524
+ return "Friendly"
525
+
526
+ def suggest_hashtags(topic, count=5):
527
+ topic_lower = topic.lower()
528
+ hashtag_map = {
529
+ "ai": ["#AI", "#MachineLearning", "#DeepLearning", "#Tech", "#Innovation"],
530
+ "career": ["#Career", "#Growth", "#ProfessionalDevelopment", "#Leadership", "#Success"],
531
+ "team": ["#Teamwork", "#Collaboration", "#Productivity", "#Management", "#Culture"],
532
+ "marketing": ["#Marketing", "#DigitalMarketing", "#ContentMarketing", "#Strategy", "#Branding"],
533
+ "sales": ["#Sales", "#Business", "#B2B", "#Revenue", "#Growth"],
534
+ "leadership": ["#Leadership", "#Management", "#ExecutiveLeadership", "#Vision", "#Strategy"],
535
+ "startup": ["#Startup", "#Entrepreneur", "#Innovation", "#Business", "#VC"],
536
+ "data": ["#DataScience", "#Analytics", "#BigData", "#DataDriven", "#Insights"]
537
+ }
538
+
539
+ for key, hashtags in hashtag_map.items():
540
+ if key in topic_lower:
541
+ return " ".join(hashtags[:count])
542
+
543
+ return "#Inspiration #Learning #Success #Innovation #Growth"
544
+
545
+ def analyze_post(post):
546
+ """Enhanced analytics"""
547
+ words = len(post.split())
548
+ paragraphs = post.count("\n\n") + 1
549
+ emojis = len(re.findall(r'[^\w\s,.]', post))
550
+ hashtags = len(re.findall(r'#\w+', post))
551
+ lines = post.count("\n") + 1
552
+
553
+ # Engagement score calculation
554
+ engagement_score = 0
555
+ if 150 <= words <= 250:
556
+ engagement_score += 30
557
+ elif words < 150:
558
+ engagement_score += 20
559
+ else:
560
+ engagement_score += 10
561
+
562
+ if 3 <= paragraphs <= 5:
563
+ engagement_score += 20
564
+
565
+ if 2 <= emojis <= 5:
566
+ engagement_score += 20
567
+
568
+ if 3 <= hashtags <= 5:
569
+ engagement_score += 15
570
+
571
+ if any(cta in post.lower() for cta in ["comment", "share", "thoughts", "agree", "experience"]):
572
+ engagement_score += 15
573
+
574
+ return {
575
+ "words": words,
576
+ "paragraphs": paragraphs,
577
+ "emojis": emojis,
578
+ "hashtags": hashtags,
579
+ "lines": lines,
580
+ "engagement_score": min(engagement_score, 100)
581
+ }
582
+
583
+ def format_analytics(analytics):
584
+ return f"""
585
+ πŸ“Š **Post Analytics**
586
+ β€’ Words: {analytics['words']}
587
+ β€’ Paragraphs: {analytics['paragraphs']}
588
+ β€’ Emojis: {analytics['emojis']}
589
+ β€’ Hashtags: {analytics['hashtags']}
590
+ β€’ Engagement Score: {analytics['engagement_score']}/100
591
+ """
592
+
593
+ def fix_emoji_encoding(text):
594
+ try:
595
+ fixed = text.encode('latin-1').decode('utf-8')
596
+ return fixed
597
+ except (UnicodeDecodeError, UnicodeEncodeError):
598
+ return text
599
+
600
+ def generate_post(topic, tone, audience, post_type, word_count, num_variations, custom_hashtags):
601
+ if not topic.strip():
602
+ return "Please enter a topic to generate a LinkedIn post."
603
+
604
+ # Determine hashtags
605
+ if custom_hashtags.strip():
606
+ hashtags = custom_hashtags
607
+ hashtags_instruction = f"Include these hashtags at the end: {hashtags}"
608
+ else:
609
+ hashtags = suggest_hashtags(topic)
610
+ hashtags_instruction = f"Suggest and include 3-5 relevant hashtags at the end"
611
+
612
+ # Select template based on post type
613
+ if post_type == "Standard Post":
614
+ template = linkedin_template
615
+ prompt = PromptTemplate(
616
+ input_variables=["topic", "tone", "audience", "post_type", "word_count", "hashtags_instruction"],
617
+ template=template
618
+ )
619
+ chain = prompt | llm | StrOutputParser()
620
+
621
+ elif post_type == "Story Post":
622
+ template = story_template
623
+ prompt = PromptTemplate(
624
+ input_variables=["topic", "tone", "word_count"],
625
+ template=template
626
+ )
627
+ chain = prompt | llm | StrOutputParser()
628
+
629
+ elif post_type == "Carousel (5 slides)":
630
+ template = carousel_template
631
+ prompt = PromptTemplate(
632
+ input_variables=["slides", "topic", "tone"],
633
+ template=template
634
+ )
635
+ chain = prompt | llm | StrOutputParser()
636
+
637
+ elif post_type == "Thread (3 posts)":
638
+ template = thread_template
639
+ prompt = PromptTemplate(
640
+ input_variables=["posts", "topic", "tone"],
641
+ template=template
642
+ )
643
+ chain = prompt | llm | StrOutputParser()
644
+
645
+ # Generate variations
646
+ variations = []
647
+ for i in range(num_variations):
648
+ if post_type == "Standard Post":
649
+ result = chain.invoke({
650
+ "topic": topic,
651
+ "tone": tone,
652
+ "audience": audience,
653
+ "post_type": post_type,
654
+ "word_count": word_count,
655
+ "hashtags_instruction": hashtags_instruction
656
+ })
657
+ elif post_type == "Story Post":
658
+ result = chain.invoke({
659
+ "topic": topic,
660
+ "tone": tone,
661
+ "word_count": word_count
662
+ })
663
+ elif post_type == "Carousel (5 slides)":
664
+ result = chain.invoke({
665
+ "slides": 5,
666
+ "topic": topic,
667
+ "tone": tone
668
+ })
669
+ elif post_type == "Thread (3 posts)":
670
+ result = chain.invoke({
671
+ "posts": 3,
672
+ "topic": topic,
673
+ "tone": tone
674
+ })
675
+
676
+ result = fix_emoji_encoding(result)
677
+ analytics = analyze_post(result)
678
+
679
+ formatted = f"**Variation {i+1}**\n\n{result}\n\n{format_analytics(analytics)}"
680
+ variations.append(formatted)
681
+
682
+ # Save to history
683
+ save_to_history(result, topic, tone, audience)
684
+
685
+ return "\n\n" + "="*80 + "\n\n".join(variations)
686
+
687
+ def rewrite_post(original_post, instruction):
688
+ """Rewrite existing post based on user instruction"""
689
+ if not original_post.strip():
690
+ return "Please paste a post to rewrite."
691
+
692
+ rewrite_template = """
693
+ You are a LinkedIn content expert. Rewrite the following post based on this instruction:
694
+
695
+ Instruction: {instruction}
696
+
697
+ Original Post:
698
+ {original_post}
699
+
700
+ Provide the rewritten version maintaining LinkedIn best practices.
701
+ """
702
+
703
+ prompt = PromptTemplate(
704
+ input_variables=["instruction", "original_post"],
705
+ template=rewrite_template
706
+ )
707
+ chain = prompt | llm | StrOutputParser()
708
+
709
+ result = chain.invoke({
710
+ "instruction": instruction,
711
+ "original_post": original_post
712
+ })
713
+
714
+ result = fix_emoji_encoding(result)
715
+ analytics = analyze_post(result)
716
+
717
+ return f"{result}\n\n{format_analytics(analytics)}"
718
+
719
+ def generate_hashtag_suggestions(topic, count):
720
+ """AI-powered hashtag suggestions"""
721
+ template = """
722
+ Generate {count} highly relevant and trending LinkedIn hashtags for this topic: {topic}
723
+
724
+ Provide hashtags that:
725
+ - Are currently popular on LinkedIn
726
+ - Mix broad and niche hashtags
727
+ - Include industry-specific tags
728
+ - Balance reach and relevance
729
+
730
+ Return only the hashtags separated by spaces, starting with #
731
+ """
732
+
733
+ prompt = PromptTemplate(
734
+ input_variables=["topic", "count"],
735
+ template=template
736
+ )
737
+ chain = prompt | llm | StrOutputParser()
738
+
739
+ result = chain.invoke({"topic": topic, "count": count})
740
+ return result.strip()
741
+
742
+ def export_current_post(content):
743
+ """Export the current post as a text file - FIXED"""
744
+ if not content or not content.strip():
745
+ return None, "❌ Nothing to export! Generate a post first."
746
+
747
+ # Remove analytics section if present
748
+ if "πŸ“Š **Post Analytics**" in content:
749
+ content = content.split("πŸ“Š **Post Analytics**")[0].strip()
750
+
751
+ filepath = export_as_text(content, "linkedin_post")
752
+ if filepath:
753
+ return filepath, "βœ… Post exported successfully!"
754
+ else:
755
+ return None, "❌ Export failed. Please try again."
756
+
757
+ def export_history_package(count):
758
+ """Export multiple posts from history - FIXED"""
759
+ try:
760
+ count = int(count)
761
+ posts_to_export = [entry['post'] for entry in post_history[:count]]
762
+
763
+ if not posts_to_export:
764
+ return None, "No posts to export. Generate some posts first!"
765
+
766
+ content = create_post_document(posts_to_export)
767
+ filepath = export_as_text(content, "linkedin_posts_package")
768
+
769
+ if filepath:
770
+ return filepath, f"βœ… Exported {len(posts_to_export)} posts successfully!"
771
+ else:
772
+ return None, "❌ Export failed. Please try again."
773
+ except Exception as e:
774
+ return None, f"❌ Export failed: {str(e)}"
775
+
776
+ # =========================
777
+ # Gradio Interface
778
+ # =========================
779
+ demo = gr.Blocks()
780
+
781
+ with demo:
782
+ gr.Markdown(
783
+ """
784
+ # LinkedIn AI Writer
785
+ ### The Complete LinkedIn Content Creation Suite with 15+ AI-Powered Features
786
+ """
787
+ )
788
+
789
+ with gr.Tabs():
790
+ # Tab 1: Generate New Post
791
+ with gr.Tab("✍️ Generate Post"):
792
+ with gr.Row():
793
+ with gr.Column(scale=1):
794
+ topic_input = gr.Textbox(
795
+ label="πŸ“ Topic",
796
+ placeholder="e.g., How AI is transforming remote work...",
797
+ lines=3
798
+ )
799
+
800
+ with gr.Row():
801
+ tone_input = gr.Dropdown(
802
+ ["Professional", "Friendly", "Inspirational", "Analytical", "Humorous"],
803
+ label="🎭 Tone",
804
+ value="Professional"
805
+ )
806
+ audience_input = gr.Dropdown(
807
+ ["General", "C-Level Executives", "Managers", "Individual Contributors", "Students", "Entrepreneurs"],
808
+ label="🎯 Target Audience",
809
+ value="General"
810
+ )
811
+
812
+ with gr.Row():
813
+ post_type_input = gr.Dropdown(
814
+ ["Standard Post", "Story Post", "Carousel (5 slides)", "Thread (3 posts)"],
815
+ label="πŸ“„ Post Type",
816
+ value="Standard Post"
817
+ )
818
+ word_count_input = gr.Slider(
819
+ minimum=100,
820
+ maximum=300,
821
+ step=25,
822
+ value=200,
823
+ label="πŸ“ Word Count"
824
+ )
825
+
826
+ num_variations_input = gr.Slider(
827
+ minimum=1,
828
+ maximum=3,
829
+ step=1,
830
+ value=1,
831
+ label="πŸ”’ Number of Variations"
832
+ )
833
+
834
+ custom_hashtags_input = gr.Textbox(
835
+ label="🏷️ Custom Hashtags (optional)",
836
+ placeholder="#AI #Innovation #Tech"
837
+ )
838
+
839
+ with gr.Row():
840
+ generate_button = gr.Button("✨ Generate Post", variant="primary", size="lg")
841
+ suggest_hashtags_button = gr.Button("πŸ’‘ Suggest Hashtags")
842
+
843
+ with gr.Column(scale=2):
844
+ output_box = gr.Textbox(
845
+ label="πŸ“„ Generated Post",
846
+ lines=25,
847
+ interactive=True,
848
+ elem_classes="output-text"
849
+ )
850
+
851
+ with gr.Row():
852
+ copy_button = gr.Button("πŸ“‹ Copy", size="sm")
853
+ export_button = gr.Button("πŸ’Ύ Export as .txt", size="sm")
854
+
855
+ with gr.Row():
856
+ copy_status = gr.Textbox(label="", interactive=False, show_label=False, lines=1)
857
+
858
+ export_file = gr.File(label="πŸ“₯ Download File", interactive=False, type="filepath")
859
+
860
+ # Tab 2: A/B Testing
861
+ with gr.Tab("πŸ”¬ A/B Testing"):
862
+ gr.Markdown("### Generate two versions of your post optimized for different strategies")
863
+ with gr.Row():
864
+ with gr.Column():
865
+ ab_topic = gr.Textbox(label="πŸ“ Topic", placeholder="Enter your topic...", lines=3)
866
+ with gr.Row():
867
+ ab_tone = gr.Dropdown(
868
+ ["Professional", "Friendly", "Inspirational", "Analytical"],
869
+ label="🎭 Tone",
870
+ value="Professional"
871
+ )
872
+ ab_audience = gr.Dropdown(
873
+ ["General", "C-Level Executives", "Managers", "Individual Contributors"],
874
+ label="🎯 Audience",
875
+ value="General"
876
+ )
877
+ ab_button = gr.Button("πŸ”¬ Generate A/B Versions", variant="primary")
878
+
879
+ with gr.Column():
880
+ ab_output = gr.Textbox(label="πŸ“Š A/B Test Results", lines=30, interactive=True)
881
+
882
+ # Tab 3: Schedule Optimizer
883
+ with gr.Tab("πŸ“… Schedule Optimizer"):
884
+ gr.Markdown("### Find the perfect time to post based on your industry and audience")
885
+ with gr.Row():
886
+ with gr.Column():
887
+ schedule_industry = gr.Textbox(
888
+ label="🏒 Your Industry",
889
+ placeholder="e.g., Technology, Marketing, Finance..."
890
+ )
891
+ schedule_location = gr.Dropdown(
892
+ ["North America (EST)", "Europe (CET)", "Asia Pacific", "Global/Mixed"],
893
+ label="🌍 Primary Audience Location",
894
+ value="North America (EST)"
895
+ )
896
+ schedule_goal = gr.Dropdown(
897
+ ["Maximum Reach", "Engagement (Comments/Shares)", "Lead Generation", "Thought Leadership"],
898
+ label="🎯 Primary Goal",
899
+ value="Maximum Reach"
900
+ )
901
+ schedule_button = gr.Button("⏰ Get Optimal Times", variant="primary")
902
+
903
+ with gr.Column():
904
+ schedule_output = gr.Textbox(label="πŸ“… Recommended Posting Schedule", lines=20)
905
+
906
+ # Tab 4: Competitor Analysis
907
+ with gr.Tab("πŸ” Competitor Analysis"):
908
+ gr.Markdown("### Analyze successful posts to understand what works")
909
+ with gr.Row():
910
+ with gr.Column():
911
+ competitor_post = gr.Textbox(
912
+ label="πŸ“ Paste Competitor's Post",
913
+ placeholder="Paste the successful post you want to analyze...",
914
+ lines=10
915
+ )
916
+ competitor_niche = gr.Textbox(
917
+ label="🎯 Your Niche/Industry",
918
+ placeholder="e.g., SaaS Marketing, Data Science..."
919
+ )
920
+ analyze_button = gr.Button("πŸ” Analyze Post", variant="primary")
921
+
922
+ with gr.Column():
923
+ competitor_output = gr.Textbox(label="πŸ“Š Analysis Results", lines=25)
924
+
925
+ # Tab 5: Image Suggestions
926
+ with gr.Tab("πŸ–ΌοΈ Image Suggestions"):
927
+ gr.Markdown("### Get AI-powered recommendations for visual content")
928
+ with gr.Row():
929
+ with gr.Column():
930
+ image_post = gr.Textbox(
931
+ label="πŸ“ Your Post Content",
932
+ placeholder="Paste your post here...",
933
+ lines=10
934
+ )
935
+ image_type = gr.Dropdown(
936
+ ["Standard Post", "Story Post", "Carousel", "Article Cover"],
937
+ label="πŸ“„ Post Type",
938
+ value="Standard Post"
939
+ )
940
+ image_button = gr.Button("🎨 Get Image Suggestions", variant="primary")
941
+
942
+ with gr.Column():
943
+ image_output = gr.Textbox(label="πŸ–ΌοΈ Visual Recommendations", lines=25)
944
+
945
+ # Tab 6: Trend Analyzer
946
+ with gr.Tab("πŸ“ˆ Trend Analyzer"):
947
+ gr.Markdown("### Discover what's trending in your industry on LinkedIn")
948
+ with gr.Row():
949
+ with gr.Column():
950
+ trend_industry = gr.Textbox(
951
+ label="🏒 Industry",
952
+ placeholder="e.g., AI, Marketing, Fintech..."
953
+ )
954
+ trend_timeframe = gr.Dropdown(
955
+ ["Last 7 days", "Last 30 days", "Last 3 months"],
956
+ label="⏰ Timeframe",
957
+ value="Last 30 days"
958
+ )
959
+ trend_button = gr.Button("πŸ“Š Analyze Trends", variant="primary")
960
+
961
+ with gr.Column():
962
+ trend_output = gr.Textbox(label="πŸ“ˆ Trending Topics & Insights", lines=25)
963
+
964
+ # Tab 7: Personal Branding
965
+ with gr.Tab("🎭 Personal Branding"):
966
+ gr.Markdown("### Create your unique brand voice for consistent LinkedIn presence")
967
+
968
+ with gr.Tabs():
969
+ with gr.Tab("Create Brand Voice"):
970
+ with gr.Row():
971
+ with gr.Column():
972
+ brand_name = gr.Textbox(label="πŸ‘€ Your Name", placeholder="John Doe")
973
+ brand_industry = gr.Textbox(label="🏒 Industry", placeholder="e.g., Digital Marketing")
974
+ brand_values = gr.Textbox(
975
+ label="πŸ’Ž Core Values",
976
+ placeholder="e.g., Innovation, Authenticity, Growth",
977
+ lines=2
978
+ )
979
+ brand_personality = gr.Textbox(
980
+ label="🎭 Personality Traits",
981
+ placeholder="e.g., Approachable, Data-driven, Creative",
982
+ lines=2
983
+ )
984
+ brand_expertise = gr.Textbox(
985
+ label="πŸŽ“ Key Expertise",
986
+ placeholder="e.g., Content Strategy, SEO, Brand Building",
987
+ lines=2
988
+ )
989
+ brand_create_button = gr.Button("✨ Create Brand Voice Guide", variant="primary")
990
+
991
+ with gr.Column():
992
+ brand_output = gr.Textbox(label="πŸ“‹ Your Brand Voice Guide", lines=30)
993
+
994
+ with gr.Tab("Apply Brand Voice"):
995
+ with gr.Row():
996
+ with gr.Column():
997
+ apply_post = gr.Textbox(
998
+ label="πŸ“ Post to Brand",
999
+ placeholder="Paste any post here to adapt it to your brand voice...",
1000
+ lines=10
1001
+ )
1002
+ apply_guide = gr.Textbox(
1003
+ label="πŸ“‹ Brand Guidelines (summary)",
1004
+ placeholder="Paste key points from your brand guide...",
1005
+ lines=5
1006
+ )
1007
+ apply_button = gr.Button("🎨 Apply Brand Voice", variant="primary")
1008
+
1009
+ with gr.Column():
1010
+ apply_output = gr.Textbox(label="✨ Branded Post", lines=20)
1011
+
1012
+ # Tab 8: Rewrite Post
1013
+ with gr.Tab("πŸ”„ Rewrite Post"):
1014
+ with gr.Row():
1015
+ with gr.Column():
1016
+ original_post_input = gr.Textbox(
1017
+ label="πŸ“ Paste Your Post",
1018
+ placeholder="Paste your LinkedIn post here...",
1019
+ lines=10
1020
+ )
1021
+ rewrite_instruction = gr.Textbox(
1022
+ label="✏️ Rewrite Instruction",
1023
+ placeholder="e.g., Make it more professional, Add storytelling, Shorten it...",
1024
+ lines=3
1025
+ )
1026
+ rewrite_button = gr.Button("πŸ”„ Rewrite Post", variant="primary")
1027
+
1028
+ with gr.Column():
1029
+ rewrite_output = gr.Textbox(
1030
+ label="✨ Rewritten Post",
1031
+ lines=15,
1032
+ interactive=True
1033
+ )
1034
+
1035
+ # Tab 9: Post History
1036
+ with gr.Tab("πŸ“š Post History"):
1037
+ gr.Markdown("### View and manage your generated posts")
1038
+ with gr.Row():
1039
+ with gr.Column(scale=2):
1040
+ history_display = gr.Textbox(
1041
+ label="πŸ“œ Recent Posts (Last 10)",
1042
+ lines=30,
1043
+ value=format_history(),
1044
+ interactive=False
1045
+ )
1046
+ refresh_history_button = gr.Button("πŸ”„ Refresh History")
1047
+
1048
+ with gr.Column(scale=1):
1049
+ gr.Markdown("### Load Post from History")
1050
+ load_post_number = gr.Number(
1051
+ label="Post Number",
1052
+ value=1,
1053
+ precision=0
1054
+ )
1055
+ load_button = gr.Button("πŸ“₯ Load Post")
1056
+ loaded_post = gr.Textbox(label="Loaded Post", lines=15, interactive=True)
1057
+
1058
+ gr.Markdown("### Export Multiple Posts")
1059
+ export_count = gr.Slider(
1060
+ minimum=1,
1061
+ maximum=10,
1062
+ step=1,
1063
+ value=5,
1064
+ label="Number of posts to export"
1065
+ )
1066
+ export_all_button = gr.Button("πŸ’Ύ Export Package")
1067
+ export_status = gr.Textbox(label="Export Status", lines=2)
1068
+ export_package_file = gr.File(label="πŸ“₯ Download Package", interactive=False, type="filepath")
1069
+
1070
+ # Tab 10: Tips & Best Practices
1071
+ with gr.Tab("πŸ’‘ Tips & Best Practices"):
1072
+ gr.Markdown(
1073
+ """
1074
+ ## 🎯 LinkedIn Best Practices
1075
+
1076
+ ### πŸ“ˆ Maximize Engagement:
1077
+ - **Hook in first 2 lines**: Grab attention immediately
1078
+ - **Use line breaks**: Make posts visually scannable
1079
+ - **Include data/numbers**: Specific metrics build credibility
1080
+ - **Add storytelling**: Personal experiences resonate
1081
+ - **End with CTA**: Ask questions, request opinions
1082
+
1083
+ ### βœ… Optimal Post Structure:
1084
+ 1. **Hook** (1-2 lines) - Stop the scroll
1085
+ 2. **Context/Story** (3-5 lines) - Build connection
1086
+ 3. **Key Points** (bullet points work well) - Deliver value
1087
+ 4. **Conclusion/Insight** (2-3 lines) - Tie it together
1088
+ 5. **CTA** (1 line + hashtags) - Drive action
1089
+
1090
+ ### 🏷️ Hashtag Strategy:
1091
+ - Use 3-5 hashtags maximum
1092
+ - Mix popular (100K+ followers) and niche (10K-50K) hashtags
1093
+ - Place at the end of post
1094
+ - Research trending hashtags in your industry weekly
1095
+
1096
+ ### πŸ“Š Best Times to Post:
1097
+ - **Tuesday-Thursday**: 9-11 AM (highest engagement)
1098
+ - **Wednesday**: Best overall day
1099
+ - **Avoid**: Weekends for B2B, early mornings, late evenings
1100
+ - **Test**: Your audience may have unique patterns
1101
+
1102
+ ### πŸ’Ž Content Types That Work:
1103
+ - **Personal stories**: Authentic experiences (highest engagement)
1104
+ - **Industry insights**: Thought leadership
1105
+ - **How-to guides**: Actionable advice
1106
+ - **Data-driven posts**: Research and statistics
1107
+ - **Controversial opinions**: Spark discussion (carefully!)
1108
+ - **Lists**: Easy to scan, high shareability
1109
+ - **Behind-the-scenes**: Show your process
1110
+
1111
+ ### 🎨 Visual Content Tips:
1112
+ - Posts with images get 2x more comments
1113
+ - Carousel posts get 1.5x more reach
1114
+ - Use high-quality, professional images
1115
+ - Infographics perform exceptionally well
1116
+ - Personal photos > stock photos
1117
+
1118
+ ### πŸš€ Growth Hacks:
1119
+ 1. **Comment within first hour**: Boost your own post
1120
+ 2. **Engage before posting**: Warm up the algorithm
1121
+ 3. **Tag relevant people**: (2-3 max, only when appropriate)
1122
+ 4. **Post consistently**: 3-5x per week minimum
1123
+ 5. **Respond to every comment**: Within first 2 hours
1124
+ 6. **Use "see more" strategically**: Hook in first 2 lines
1125
+ 7. **Write for mobile**: Short paragraphs, more line breaks
1126
+
1127
+ ### πŸ“ Optimal Lengths:
1128
+ - **Standard posts**: 150-250 words (sweet spot: 200)
1129
+ - **Long-form**: 1,300-2,000 words (for thought leadership)
1130
+ - **Carousels**: 10-15 slides maximum
1131
+ - **Threads**: 3-5 posts
1132
+
1133
+ ### ⚠️ What to Avoid:
1134
+ - External links (post in first comment instead)
1135
+ - Too many hashtags (looks spammy)
1136
+ - Generic content (be specific!)
1137
+ - Overly promotional content
1138
+ - Inconsistent posting (algorithm penalizes)
1139
+ - Ignoring comments (kills engagement)
1140
+
1141
+ ### πŸŽ“ Advanced Tips:
1142
+ - Use the **"3-3-3 Rule"**: 3 posts/week, 3 comments on others' posts/day, 3 meaningful connections/day
1143
+ - **Native video** gets 5x more engagement than YouTube links
1144
+ - **Ask questions** at the end - increases comments by 50%
1145
+ - **Use emojis strategically** - but not in excess
1146
+ - **Write for skimmers**: Bullets, bold, line breaks
1147
+ """
1148
+ )
1149
+
1150
+ # =========================
1151
+ # Event Handlers
1152
+ # =========================
1153
+
1154
+ # Generate Post
1155
+ generate_button.click(
1156
+ fn=generate_post,
1157
+ inputs=[
1158
+ topic_input,
1159
+ tone_input,
1160
+ audience_input,
1161
+ post_type_input,
1162
+ word_count_input,
1163
+ num_variations_input,
1164
+ custom_hashtags_input
1165
+ ],
1166
+ outputs=output_box
1167
+ )
1168
+
1169
+ # Suggest Hashtags
1170
+ suggest_hashtags_button.click(
1171
+ fn=lambda topic: generate_hashtag_suggestions(topic, 5),
1172
+ inputs=topic_input,
1173
+ outputs=custom_hashtags_input
1174
+ )
1175
+
1176
+ # A/B Testing
1177
+ ab_button.click(
1178
+ fn=generate_ab_versions,
1179
+ inputs=[ab_topic, ab_tone, ab_audience],
1180
+ outputs=ab_output
1181
+ )
1182
+
1183
+ # Schedule Optimizer
1184
+ schedule_button.click(
1185
+ fn=get_best_posting_times,
1186
+ inputs=[schedule_industry, schedule_location, schedule_goal],
1187
+ outputs=schedule_output
1188
+ )
1189
+
1190
+ # Competitor Analysis
1191
+ analyze_button.click(
1192
+ fn=analyze_competitor_post,
1193
+ inputs=[competitor_post, competitor_niche],
1194
+ outputs=competitor_output
1195
+ )
1196
+
1197
+ # Image Suggestions
1198
+ image_button.click(
1199
+ fn=suggest_images,
1200
+ inputs=[image_post, image_type],
1201
+ outputs=image_output
1202
+ )
1203
+
1204
+ # Trend Analyzer
1205
+ trend_button.click(
1206
+ fn=analyze_linkedin_trends,
1207
+ inputs=[trend_industry, trend_timeframe],
1208
+ outputs=trend_output
1209
+ )
1210
+
1211
+ # Personal Branding
1212
+ brand_create_button.click(
1213
+ fn=create_brand_voice,
1214
+ inputs=[brand_name, brand_industry, brand_values, brand_personality, brand_expertise],
1215
+ outputs=brand_output
1216
+ )
1217
+
1218
+ apply_button.click(
1219
+ fn=apply_brand_voice,
1220
+ inputs=[apply_post, apply_guide],
1221
+ outputs=apply_output
1222
+ )
1223
+
1224
+ # Rewrite Post
1225
+ rewrite_button.click(
1226
+ fn=rewrite_post,
1227
+ inputs=[original_post_input, rewrite_instruction],
1228
+ outputs=rewrite_output
1229
+ )
1230
+
1231
+ # Post History
1232
+ refresh_history_button.click(
1233
+ fn=format_history,
1234
+ inputs=[],
1235
+ outputs=history_display
1236
+ )
1237
+
1238
+ load_button.click(
1239
+ fn=get_post_from_history,
1240
+ inputs=load_post_number,
1241
+ outputs=loaded_post
1242
+ )
1243
+
1244
+ # Export History Package - FIXED
1245
+ export_all_button.click(
1246
+ fn=export_history_package,
1247
+ inputs=export_count,
1248
+ outputs=[export_package_file, export_status]
1249
+ )
1250
+
1251
+ # Copy to Clipboard
1252
+ copy_js = """
1253
+ async function() {
1254
+ const textArea = document.querySelector('.output-text textarea');
1255
+ if (textArea && textArea.value) {
1256
+ try {
1257
+ await navigator.clipboard.writeText(textArea.value);
1258
+ return "βœ… Copied successfully!";
1259
+ } catch (err) {
1260
+ textArea.select();
1261
+ document.execCommand('copy');
1262
+ return "βœ… Copied successfully!";
1263
+ }
1264
+ }
1265
+ return "❌ Nothing to copy! Generate a post first.";
1266
+ }
1267
+ """
1268
+
1269
+ copy_button.click(
1270
+ fn=None,
1271
+ inputs=None,
1272
+ outputs=copy_status,
1273
+ js=copy_js
1274
+ )
1275
+
1276
+ # Export Current Post - FIXED
1277
+ export_button.click(
1278
+ fn=export_current_post,
1279
+ inputs=[output_box],
1280
+ outputs=[export_file, copy_status]
1281
+ )
1282
+
1283
+ # Launch the application
1284
+ print("\n" + "="*80)
1285
+ print("πŸš€ LAUNCHING LINKEDIN AI WRITER PRO - ULTIMATE EDITION")
1286
+ print("="*80 + "\n")
1287
+
1288
+ demo.launch(share=True, debug=True)