-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenhanced_gmail_outbound.py
More file actions
360 lines (293 loc) · 16.1 KB
/
enhanced_gmail_outbound.py
File metadata and controls
360 lines (293 loc) · 16.1 KB
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
#!/usr/bin/env python3
"""
Enhanced Gmail-style personalized outbound with deep LinkedIn personalization.
Each email is uniquely crafted based on the person's LinkedIn profile data.
"""
import asyncio
import csv
import json
import random
from pathlib import Path
from linkedin_scraper import Person
from linkedin_mcp_server.error_handler import safe_get_driver
class LinkedInPersonalizer:
"""Advanced personalization engine for LinkedIn data."""
def __init__(self):
self.industry_insights = {
'security': [
"the increasing importance of zero-trust architecture",
"the challenges of securing cloud-native applications",
"the complexity of compliance in multi-cloud environments"
],
'fintech': [
"the rapid evolution of digital payment systems",
"the regulatory challenges in financial technology",
"the importance of scalable infrastructure for financial services"
],
'healthcare': [
"the critical need for HIPAA-compliant cloud solutions",
"the challenges of healthcare data interoperability",
"the importance of secure patient data management"
],
'startup': [
"the challenges of scaling infrastructure as you grow",
"the importance of cost-effective cloud solutions for startups",
"the need for reliable, scalable architecture from day one"
]
}
self.role_specific_pain_points = {
'ceo': "balancing growth with operational efficiency",
'cto': "making strategic technology decisions that scale",
'engineering manager': "optimizing team productivity and system reliability",
'founder': "building robust infrastructure while managing costs",
'vp engineering': "scaling engineering teams and systems effectively",
'director': "aligning technology initiatives with business goals"
}
def analyze_profile(self, person_data, csv_data):
"""Analyze LinkedIn profile for personalization opportunities."""
analysis = {
'industry_context': None,
'role_pain_point': None,
'company_stage': None,
'tech_indicators': [],
'recent_activity_hook': None,
'experience_hook': None
}
# Analyze industry context
about = (person_data.get('about', '') + ' ' + csv_data.get('Company', '')).lower()
for industry, insights in self.industry_insights.items():
if industry in about:
analysis['industry_context'] = random.choice(insights)
break
# Analyze role for pain points
title = (person_data.get('job_title', '') + ' ' + csv_data.get('Title', '')).lower()
for role, pain_point in self.role_specific_pain_points.items():
if role in title:
analysis['role_pain_point'] = pain_point
break
# Detect tech indicators
tech_keywords = ['aws', 'cloud', 'kubernetes', 'docker', 'microservices', 'api', 'devops']
for keyword in tech_keywords:
if keyword in about:
analysis['tech_indicators'].append(keyword)
# Analyze recent activity
recent_activity = person_data.get('recent_activity', '')
if recent_activity:
if len(recent_activity) > 50:
analysis['recent_activity_hook'] = recent_activity[:100] + "..."
# Analyze experience for hooks
experiences = person_data.get('experiences', [])
if experiences:
recent_exp = experiences[0]
if recent_exp.get('position_title') and recent_exp.get('institution_name'):
analysis['experience_hook'] = f"{recent_exp['position_title']} at {recent_exp['institution_name']}"
return analysis
def generate_personalized_opening(self, analysis, person_data, csv_data):
"""Generate a highly personalized opening line."""
name = person_data.get('name', csv_data.get('First Name', ''))
company = csv_data.get('Company', '')
openings = []
# Recent activity based opening
if analysis['recent_activity_hook']:
openings.append(f"Hi {name}, I saw your recent post about {analysis['recent_activity_hook'].lower()} and it really resonated with me.")
# Industry context opening
if analysis['industry_context']:
openings.append(f"Hi {name}, given your work at {company}, you probably understand {analysis['industry_context']}.")
# Role-specific opening
if analysis['role_pain_point']:
openings.append(f"Hi {name}, as someone in your role, you're likely focused on {analysis['role_pain_point']}.")
# Experience-based opening
if analysis['experience_hook']:
openings.append(f"Hi {name}, I was impressed by your background, particularly your experience as {analysis['experience_hook']}.")
# Tech-focused opening
if analysis['tech_indicators']:
tech_list = ', '.join(analysis['tech_indicators'][:2])
openings.append(f"Hi {name}, I noticed your experience with {tech_list} and thought you might be interested in what we're doing at AllCode.")
# Fallback opening
if not openings:
openings.append(f"Hi {name}, I came across your profile at {company} and was impressed by what you're building there.")
return random.choice(openings)
def generate_value_proposition(self, analysis, csv_data):
"""Generate a tailored value proposition."""
company = csv_data.get('Company', '')
value_props = []
# Industry-specific value props
if 'security' in (csv_data.get('Company', '') + csv_data.get('Title', '')).lower():
value_props.append(f"We've helped security companies like {company} build more resilient, scalable infrastructure that can handle the demands of modern threat detection and response.")
elif 'fintech' in (csv_data.get('Company', '') + csv_data.get('Title', '')).lower():
value_props.append(f"We specialize in helping fintech companies build compliant, scalable infrastructure that can handle high-volume transactions while maintaining security.")
# Tech-focused value props
if analysis['tech_indicators']:
value_props.append(f"Given your experience with {', '.join(analysis['tech_indicators'][:2])}, you'd probably appreciate our approach to cloud-native architecture and DevOps automation.")
# Role-specific value props
if 'ceo' in csv_data.get('Title', '').lower() or 'founder' in csv_data.get('Title', '').lower():
value_props.append(f"We've helped companies like {company} reduce their infrastructure costs by 30-40% while improving reliability and scalability.")
elif 'engineering' in csv_data.get('Title', '').lower():
value_props.append(f"We focus on helping engineering teams like yours build more efficient, maintainable systems that scale with your business.")
# Fallback value prop
if not value_props:
value_props.append(f"We specialize in AWS cloud solutions and have helped companies like {company} streamline their infrastructure and accelerate their development cycles.")
return random.choice(value_props)
def generate_call_to_action(self, analysis, csv_data):
"""Generate a natural, role-appropriate call to action."""
ctas = []
# Role-specific CTAs
if 'ceo' in csv_data.get('Title', '').lower() or 'founder' in csv_data.get('Title', '').lower():
ctas.append("Would you be open to a brief conversation about how we might help you scale more efficiently?")
ctas.append("Worth a quick 15-minute call to discuss potential synergies?")
elif 'engineering' in csv_data.get('Title', '').lower():
ctas.append("Would you have 15 minutes to discuss some of the technical challenges you're facing?")
ctas.append("I'd love to share some insights from similar companies we've worked with - worth a quick call?")
# General CTAs
ctas.extend([
"Would you be interested in a brief conversation to explore how we might be able to help?",
"Worth a quick chat to see if there's a fit?",
"Would you have 15 minutes this week for a quick call?"
])
return random.choice(ctas)
async def generate_enhanced_gmail_outbound(limit=10):
"""Generate highly personalized Gmail-style emails using advanced LinkedIn analysis."""
# Find CSV file
csv_files = list(Path(".").glob("AllCode*.csv"))
if not csv_files:
print("❌ No AllCode CSV file found")
return
csv_file = csv_files[0]
print(f"📁 Processing: {csv_file}")
driver = safe_get_driver()
personalizer = LinkedInPersonalizer()
results = []
with open(csv_file, 'r', encoding='utf-8', errors='ignore') as file:
reader = csv.DictReader(file)
all_rows = list(reader)
# Filter contacts with LinkedIn profiles and emails
valid_rows = [row for row in all_rows
if row.get("Person Linkedin Url", "").strip()
and row.get("Email", "").strip()]
# Randomly select from valid contacts
selected_rows = random.sample(valid_rows, min(limit, len(valid_rows)))
print(f"📊 Found {len(valid_rows)} valid contacts, selecting {len(selected_rows)}")
for row_num, row in enumerate(selected_rows, 1):
linkedin_url = row.get("Person Linkedin Url", "").strip()
first_name = row.get("First Name", "")
last_name = row.get("Last Name", "")
title = row.get("Title", "")
email = row.get("Email", "")
company = row.get("Company", "")
full_name = f"{first_name} {last_name}".strip()
print(f"\n👤 Contact {row_num}: {full_name} at {company}")
try:
# Check cache first
cache_file = f"cache_{linkedin_url.split('/')[-2]}.json"
if Path(cache_file).exists():
print("📋 Using cached profile...")
with open(cache_file, 'r') as f:
person_data = json.load(f)
else:
print("🔍 Scraping LinkedIn profile...")
person = Person(linkedin_url, driver=driver, close_on_complete=False)
# Get recent activity
recent_activity = ""
try:
print("📝 Checking recent activity...")
driver.get(linkedin_url + "recent-activity/all/")
import time
time.sleep(3)
activities = driver.find_elements("css selector", "[data-id*='urn:li:activity']")[:2]
if activities:
for activity in activities:
try:
text_elem = activity.find_element("css selector", ".feed-shared-text")
if text_elem and text_elem.text:
recent_activity = text_elem.text[:200]
break
except:
continue
except Exception as e:
print(f"⚠️ Could not fetch recent activity: {e}")
# Cache the profile data
person_data = {
'name': person.name,
'company': person.company,
'job_title': person.job_title,
'about': person.about[:500] if person.about else "",
'recent_activity': recent_activity,
'experiences': [
{
'position_title': exp.position_title,
'institution_name': exp.institution_name,
'duration': exp.duration,
'description': exp.description[:200] if exp.description else ""
} for exp in person.experiences[:3]
] if person.experiences else []
}
with open(cache_file, 'w') as f:
json.dump(person_data, f, indent=2)
# Analyze profile for personalization
print("🧠 Analyzing profile for personalization...")
analysis = personalizer.analyze_profile(person_data, row)
# Generate highly personalized email
opening = personalizer.generate_personalized_opening(analysis, person_data, row)
value_prop = personalizer.generate_value_proposition(analysis, row)
cta = personalizer.generate_call_to_action(analysis, row)
# Generate subject line based on analysis
subject_options = []
if analysis['tech_indicators']:
subject_options.append(f"Your {analysis['tech_indicators'][0].upper()} setup at {company}")
if analysis['industry_context']:
subject_options.append(f"Cloud solutions for {company}")
subject_options.extend([
f"Quick question about {company}'s infrastructure",
f"{person_data.get('name', first_name)} - AWS optimization opportunity",
f"Helping {company} scale more efficiently"
])
subject = random.choice(subject_options)
# Construct the email
email_content = f"""Subject: {subject}
{opening}
{value_prop}
{cta}
Best regards,
Andreas Garcia
AllCode
andreas@allcode.com
(415) 890-6431"""
contact_result = {
"row_number": row_num,
"contact_info": {
"name": full_name,
"email": email,
"company": company,
"title": title
},
"linkedin_data": person_data,
"personalization_analysis": analysis,
"enhanced_gmail_email": email_content
}
results.append(contact_result)
print(f"✅ Enhanced personalized email generated!")
except Exception as e:
print(f"❌ Error processing {linkedin_url}: {e}")
continue
# Save results
output_file = "enhanced_gmail_outbound_results.json"
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(results, f, indent=2, ensure_ascii=False)
print(f"\n🎉 Enhanced Gmail outbound generation complete!")
print(f"📊 Generated {len(results)} highly personalized emails")
print(f"💾 Results saved to: {output_file}")
# Display sample emails
if results:
for i, result in enumerate(results[:2], 1):
print(f"\n📧 Sample Email {i} for {result['contact_info']['name']}:")
print("=" * 80)
print(result['enhanced_gmail_email'])
print("=" * 80)
print(f"🧠 Personalization used: {result['personalization_analysis']}")
print("-" * 40)
return results
if __name__ == "__main__":
print("🚀 Enhanced Gmail-Style LinkedIn Outbound Generator")
print("=" * 60)
# Generate highly personalized emails
asyncio.run(generate_enhanced_gmail_outbound(limit=10))