@xuda.io/ai_module 1.1.4766 → 1.1.4767
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.mjs +2715 -569
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -9241,640 +9241,640 @@ Category:`;
|
|
|
9241
9241
|
// }
|
|
9242
9242
|
// };
|
|
9243
9243
|
|
|
9244
|
-
export const get_person_info = async function (uid, name, email, account_profile_info, email_context = '') {
|
|
9245
|
-
|
|
9244
|
+
// export const get_person_info = async function (uid, name, email, account_profile_info, email_context = '') {
|
|
9245
|
+
// // ========== HELPER FUNCTIONS ==========
|
|
9246
9246
|
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9247
|
+
// async function hashString(str) {
|
|
9248
|
+
// // Simple hash for context tracking
|
|
9249
|
+
// const encoder = new TextEncoder();
|
|
9250
|
+
// const data = encoder.encode(str);
|
|
9251
|
+
// const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
9252
|
+
// const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
9253
|
+
// return hashArray
|
|
9254
|
+
// .map((b) => b.toString(16).padStart(2, '0'))
|
|
9255
|
+
// .join('')
|
|
9256
|
+
// .substring(0, 16);
|
|
9257
|
+
// }
|
|
9258
9258
|
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9259
|
+
// function processPersonInfo(data, originalName, originalEmail, emailContext) {
|
|
9260
|
+
// // Ensure basic fields are populated
|
|
9261
|
+
// if (!data.person_first_name || !data.person_last_name) {
|
|
9262
|
+
// const nameParts = originalName.trim().split(/\s+/);
|
|
9263
|
+
// data.person_first_name = data.person_first_name || nameParts[0] || '';
|
|
9264
|
+
// data.person_last_name = data.person_last_name || nameParts.slice(1).join(' ') || '';
|
|
9265
|
+
// }
|
|
9266
9266
|
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9267
|
+
// // Set full name if not provided
|
|
9268
|
+
// if (!data.person_full_name) {
|
|
9269
|
+
// data.person_full_name = `${data.person_first_name} ${data.person_last_name}`.trim();
|
|
9270
|
+
// }
|
|
9271
9271
|
|
|
9272
|
-
|
|
9273
|
-
|
|
9272
|
+
// // Ensure email is set
|
|
9273
|
+
// data.person_email = data.person_email || originalEmail;
|
|
9274
9274
|
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9275
|
+
// // Process demographic information
|
|
9276
|
+
// data.person_age = processAge(data.person_age);
|
|
9277
|
+
// data.person_gender = data.person_gender || 'unknown';
|
|
9278
|
+
// data.person_nationality = processNationality(data.person_nationality);
|
|
9279
9279
|
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9280
|
+
// // Standardize location fields
|
|
9281
|
+
// data.person_location_city = cleanText(data.person_location_city);
|
|
9282
|
+
// data.person_location_state = cleanStateProvince(data.person_location_state);
|
|
9283
|
+
// data.person_location_country = processCountryCode(data.person_location_country);
|
|
9284
9284
|
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9285
|
+
// // Format full location
|
|
9286
|
+
// if (!data.person_location_full || data.person_location_full === 'Not available') {
|
|
9287
|
+
// const locationParts = [];
|
|
9288
|
+
// if (data.person_location_city && data.person_location_city !== 'Not available') {
|
|
9289
|
+
// locationParts.push(data.person_location_city);
|
|
9290
|
+
// }
|
|
9291
|
+
// if (data.person_location_state && data.person_location_state !== 'Not available') {
|
|
9292
|
+
// locationParts.push(data.person_location_state);
|
|
9293
|
+
// }
|
|
9294
|
+
// if (data.person_location_country && data.person_location_country !== 'Not available') {
|
|
9295
|
+
// locationParts.push(countryCodeToName(data.person_location_country));
|
|
9296
|
+
// }
|
|
9297
|
+
// data.person_location_full = locationParts.length > 0 ? locationParts.join(', ') : 'Not available';
|
|
9298
|
+
// }
|
|
9299
9299
|
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9300
|
+
// // Clean and validate URLs
|
|
9301
|
+
// data.person_linkedin = validateURL(data.person_linkedin);
|
|
9302
|
+
// data.person_twitter = validateURL(data.person_twitter);
|
|
9303
|
+
// data.person_github = validateURL(data.person_github);
|
|
9304
|
+
// data.person_website = validateURL(data.person_website);
|
|
9305
|
+
|
|
9306
|
+
// // Clean phone number
|
|
9307
|
+
// data.person_phone = validatePhoneNumber(data.person_phone);
|
|
9308
|
+
|
|
9309
|
+
// // Ensure arrays are properly formatted
|
|
9310
|
+
// data.person_skills = ensureArray(data.person_skills);
|
|
9311
|
+
// data.person_expertise = ensureArray(data.person_expertise);
|
|
9312
|
+
// data.person_education = ensureArray(data.person_education);
|
|
9313
|
+
// data.person_languages = ensureArray(data.person_languages);
|
|
9314
|
+
// data.person_interests = ensureArray(data.person_interests);
|
|
9315
|
+
// data.person_context_insights = ensureArray(data.person_context_insights);
|
|
9316
|
+
|
|
9317
|
+
// // Calculate additional metadata
|
|
9318
|
+
// data.person_name_initials = getInitials(data.person_first_name, data.person_last_name);
|
|
9319
|
+
// data.person_age_range = calculateAgeRange(data.person_age);
|
|
9320
|
+
// data.person_seniority_score = calculateSeniorityScore(data);
|
|
9321
|
+
// data.person_pronouns = getPronounsFromGender(data.person_gender);
|
|
9322
|
+
|
|
9323
|
+
// // Extract insights from email context
|
|
9324
|
+
// if (emailContext && !data.person_context_insights) {
|
|
9325
|
+
// data.person_context_insights = extractContextInsights(emailContext);
|
|
9326
|
+
// }
|
|
9305
9327
|
|
|
9306
|
-
|
|
9307
|
-
|
|
9328
|
+
// // Create structured objects for easier access
|
|
9329
|
+
// data.person_demographics = {
|
|
9330
|
+
// age: data.person_age,
|
|
9331
|
+
// age_range: data.person_age_range,
|
|
9332
|
+
// gender: data.person_gender,
|
|
9333
|
+
// pronouns: data.person_pronouns,
|
|
9334
|
+
// nationality: data.person_nationality,
|
|
9335
|
+
// nationality_name: countryCodeToName(data.person_nationality),
|
|
9336
|
+
// };
|
|
9308
9337
|
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9338
|
+
// data.person_contact = {
|
|
9339
|
+
// email: data.person_email,
|
|
9340
|
+
// phone: data.person_phone,
|
|
9341
|
+
// location: {
|
|
9342
|
+
// city: data.person_location_city,
|
|
9343
|
+
// state: data.person_location_state,
|
|
9344
|
+
// country: data.person_location_country,
|
|
9345
|
+
// country_name: countryCodeToName(data.person_location_country),
|
|
9346
|
+
// full: data.person_location_full,
|
|
9347
|
+
// },
|
|
9348
|
+
// };
|
|
9316
9349
|
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9350
|
+
// data.person_professional = {
|
|
9351
|
+
// title: data.person_title,
|
|
9352
|
+
// company: data.person_company,
|
|
9353
|
+
// industry: data.person_industry,
|
|
9354
|
+
// career_level: data.person_career_level,
|
|
9355
|
+
// years_experience: data.person_years_experience,
|
|
9356
|
+
// skills: data.person_skills,
|
|
9357
|
+
// expertise: data.person_expertise,
|
|
9358
|
+
// education: data.person_education,
|
|
9359
|
+
// };
|
|
9322
9360
|
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9361
|
+
// data.person_online = {
|
|
9362
|
+
// linkedin: data.person_linkedin,
|
|
9363
|
+
// twitter: data.person_twitter,
|
|
9364
|
+
// github: data.person_github,
|
|
9365
|
+
// website: data.person_website,
|
|
9366
|
+
// other: data.person_social_other || [],
|
|
9367
|
+
// };
|
|
9327
9368
|
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
nationality_name: countryCodeToName(data.person_nationality),
|
|
9336
|
-
};
|
|
9369
|
+
// // Add context metadata
|
|
9370
|
+
// data.context_analysis = {
|
|
9371
|
+
// has_context: !!emailContext,
|
|
9372
|
+
// context_length: emailContext?.length || 0,
|
|
9373
|
+
// extracted_insights: data.person_context_insights || [],
|
|
9374
|
+
// context_useful: data.person_has_context_hints || false,
|
|
9375
|
+
// };
|
|
9337
9376
|
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
phone: data.person_phone,
|
|
9341
|
-
location: {
|
|
9342
|
-
city: data.person_location_city,
|
|
9343
|
-
state: data.person_location_state,
|
|
9344
|
-
country: data.person_location_country,
|
|
9345
|
-
country_name: countryCodeToName(data.person_location_country),
|
|
9346
|
-
full: data.person_location_full,
|
|
9347
|
-
},
|
|
9348
|
-
};
|
|
9377
|
+
// // Add timestamp
|
|
9378
|
+
// data.retrieved_at = new Date().toISOString();
|
|
9349
9379
|
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
company: data.person_company,
|
|
9353
|
-
industry: data.person_industry,
|
|
9354
|
-
career_level: data.person_career_level,
|
|
9355
|
-
years_experience: data.person_years_experience,
|
|
9356
|
-
skills: data.person_skills,
|
|
9357
|
-
expertise: data.person_expertise,
|
|
9358
|
-
education: data.person_education,
|
|
9359
|
-
};
|
|
9380
|
+
// return data;
|
|
9381
|
+
// }
|
|
9360
9382
|
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
website: data.person_website,
|
|
9366
|
-
other: data.person_social_other || [],
|
|
9367
|
-
};
|
|
9383
|
+
// function processAge(ageInput) {
|
|
9384
|
+
// if (!ageInput || ageInput === 'unknown' || ageInput === 'Not available') {
|
|
9385
|
+
// return 'unknown';
|
|
9386
|
+
// }
|
|
9368
9387
|
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
context_length: emailContext?.length || 0,
|
|
9373
|
-
extracted_insights: data.person_context_insights || [],
|
|
9374
|
-
context_useful: data.person_has_context_hints || false,
|
|
9375
|
-
};
|
|
9388
|
+
// if (typeof ageInput === 'number') {
|
|
9389
|
+
// return Math.max(18, Math.min(100, ageInput));
|
|
9390
|
+
// }
|
|
9376
9391
|
|
|
9377
|
-
|
|
9378
|
-
|
|
9392
|
+
// if (typeof ageInput === 'string') {
|
|
9393
|
+
// // Parse age ranges like "30-40", "35+", "mid-30s"
|
|
9394
|
+
// const rangeMatch = ageInput.match(/(\d+)-(\d+)/);
|
|
9395
|
+
// if (rangeMatch) {
|
|
9396
|
+
// const avg = Math.floor((parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2);
|
|
9397
|
+
// return avg;
|
|
9398
|
+
// }
|
|
9379
9399
|
|
|
9380
|
-
|
|
9381
|
-
|
|
9400
|
+
// const numberMatch = ageInput.match(/\d+/);
|
|
9401
|
+
// if (numberMatch) {
|
|
9402
|
+
// const age = parseInt(numberMatch[0]);
|
|
9403
|
+
// if (age >= 18 && age <= 100) return age;
|
|
9404
|
+
// }
|
|
9382
9405
|
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9406
|
+
// // Handle textual age descriptions
|
|
9407
|
+
// const text = ageInput.toLowerCase();
|
|
9408
|
+
// if (text.includes('twent') || text.includes('20')) return 25;
|
|
9409
|
+
// if (text.includes('thirt') || text.includes('30')) return 35;
|
|
9410
|
+
// if (text.includes('fort') || text.includes('40')) return 45;
|
|
9411
|
+
// if (text.includes('fift') || text.includes('50')) return 55;
|
|
9412
|
+
// if (text.includes('sixt') || text.includes('60')) return 65;
|
|
9413
|
+
// }
|
|
9387
9414
|
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
}
|
|
9415
|
+
// return 'unknown';
|
|
9416
|
+
// }
|
|
9391
9417
|
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
const rangeMatch = ageInput.match(/(\d+)-(\d+)/);
|
|
9395
|
-
if (rangeMatch) {
|
|
9396
|
-
const avg = Math.floor((parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2);
|
|
9397
|
-
return avg;
|
|
9398
|
-
}
|
|
9418
|
+
// function calculateAgeRange(age) {
|
|
9419
|
+
// if (age === 'unknown' || typeof age !== 'number') return 'Unknown';
|
|
9399
9420
|
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9421
|
+
// if (age < 25) return '18-24';
|
|
9422
|
+
// if (age < 30) return '25-29';
|
|
9423
|
+
// if (age < 35) return '30-34';
|
|
9424
|
+
// if (age < 40) return '35-39';
|
|
9425
|
+
// if (age < 50) return '40-49';
|
|
9426
|
+
// if (age < 60) return '50-59';
|
|
9427
|
+
// return '60+';
|
|
9428
|
+
// }
|
|
9405
9429
|
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
if (text.includes('fort') || text.includes('40')) return 45;
|
|
9411
|
-
if (text.includes('fift') || text.includes('50')) return 55;
|
|
9412
|
-
if (text.includes('sixt') || text.includes('60')) return 65;
|
|
9413
|
-
}
|
|
9430
|
+
// function processNationality(nationality) {
|
|
9431
|
+
// if (!nationality || nationality.toLowerCase() === 'unknown' || nationality.toLowerCase() === 'not available') {
|
|
9432
|
+
// return 'unknown';
|
|
9433
|
+
// }
|
|
9414
9434
|
|
|
9415
|
-
|
|
9416
|
-
|
|
9435
|
+
// // Convert to uppercase for country codes
|
|
9436
|
+
// const upper = nationality.toUpperCase();
|
|
9417
9437
|
|
|
9418
|
-
|
|
9419
|
-
|
|
9438
|
+
// // Map common variations to ISO codes
|
|
9439
|
+
// const countryMap = {
|
|
9440
|
+
// USA: 'US',
|
|
9441
|
+
// 'UNITED STATES': 'US',
|
|
9442
|
+
// AMERICA: 'US',
|
|
9443
|
+
// UK: 'GB',
|
|
9444
|
+
// 'UNITED KINGDOM': 'GB',
|
|
9445
|
+
// ENGLAND: 'GB',
|
|
9446
|
+
// SCOTLAND: 'GB',
|
|
9447
|
+
// CANADA: 'CA',
|
|
9448
|
+
// AUSTRALIA: 'AU',
|
|
9449
|
+
// INDIA: 'IN',
|
|
9450
|
+
// GERMANY: 'DE',
|
|
9451
|
+
// FRANCE: 'FR',
|
|
9452
|
+
// CHINA: 'CN',
|
|
9453
|
+
// JAPAN: 'JP',
|
|
9454
|
+
// BRAZIL: 'BR',
|
|
9455
|
+
// MEXICO: 'MX',
|
|
9456
|
+
// SPAIN: 'ES',
|
|
9457
|
+
// ITALY: 'IT',
|
|
9458
|
+
// NETHERLANDS: 'NL',
|
|
9459
|
+
// SWEDEN: 'SE',
|
|
9460
|
+
// SWITZERLAND: 'CH',
|
|
9461
|
+
// 'SOUTH KOREA': 'KR',
|
|
9462
|
+
// RUSSIA: 'RU',
|
|
9463
|
+
// };
|
|
9420
9464
|
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
if (age < 40) return '35-39';
|
|
9425
|
-
if (age < 50) return '40-49';
|
|
9426
|
-
if (age < 60) return '50-59';
|
|
9427
|
-
return '60+';
|
|
9428
|
-
}
|
|
9465
|
+
// if (countryMap[upper]) {
|
|
9466
|
+
// return countryMap[upper];
|
|
9467
|
+
// }
|
|
9429
9468
|
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9469
|
+
// // If it's already a 2-letter code, return it
|
|
9470
|
+
// if (/^[A-Z]{2}$/.test(upper)) {
|
|
9471
|
+
// return upper;
|
|
9472
|
+
// }
|
|
9434
9473
|
|
|
9435
|
-
|
|
9436
|
-
|
|
9474
|
+
// return 'unknown';
|
|
9475
|
+
// }
|
|
9437
9476
|
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
AMERICA: 'US',
|
|
9443
|
-
UK: 'GB',
|
|
9444
|
-
'UNITED KINGDOM': 'GB',
|
|
9445
|
-
ENGLAND: 'GB',
|
|
9446
|
-
SCOTLAND: 'GB',
|
|
9447
|
-
CANADA: 'CA',
|
|
9448
|
-
AUSTRALIA: 'AU',
|
|
9449
|
-
INDIA: 'IN',
|
|
9450
|
-
GERMANY: 'DE',
|
|
9451
|
-
FRANCE: 'FR',
|
|
9452
|
-
CHINA: 'CN',
|
|
9453
|
-
JAPAN: 'JP',
|
|
9454
|
-
BRAZIL: 'BR',
|
|
9455
|
-
MEXICO: 'MX',
|
|
9456
|
-
SPAIN: 'ES',
|
|
9457
|
-
ITALY: 'IT',
|
|
9458
|
-
NETHERLANDS: 'NL',
|
|
9459
|
-
SWEDEN: 'SE',
|
|
9460
|
-
SWITZERLAND: 'CH',
|
|
9461
|
-
'SOUTH KOREA': 'KR',
|
|
9462
|
-
RUSSIA: 'RU',
|
|
9463
|
-
};
|
|
9477
|
+
// function processCountryCode(country) {
|
|
9478
|
+
// if (!country || country.toLowerCase() === 'not available') {
|
|
9479
|
+
// return 'unknown';
|
|
9480
|
+
// }
|
|
9464
9481
|
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
}
|
|
9482
|
+
// return processNationality(country);
|
|
9483
|
+
// }
|
|
9468
9484
|
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
return upper;
|
|
9472
|
-
}
|
|
9485
|
+
// function countryCodeToName(code) {
|
|
9486
|
+
// if (!code || code === 'unknown') return 'Unknown';
|
|
9473
9487
|
|
|
9474
|
-
|
|
9475
|
-
|
|
9488
|
+
// const countryNames = {
|
|
9489
|
+
// US: 'United States',
|
|
9490
|
+
// GB: 'United Kingdom',
|
|
9491
|
+
// CA: 'Canada',
|
|
9492
|
+
// AU: 'Australia',
|
|
9493
|
+
// DE: 'Germany',
|
|
9494
|
+
// FR: 'France',
|
|
9495
|
+
// JP: 'Japan',
|
|
9496
|
+
// CN: 'China',
|
|
9497
|
+
// IN: 'India',
|
|
9498
|
+
// BR: 'Brazil',
|
|
9499
|
+
// MX: 'Mexico',
|
|
9500
|
+
// ES: 'Spain',
|
|
9501
|
+
// IT: 'Italy',
|
|
9502
|
+
// NL: 'Netherlands',
|
|
9503
|
+
// SE: 'Sweden',
|
|
9504
|
+
// CH: 'Switzerland',
|
|
9505
|
+
// KR: 'South Korea',
|
|
9506
|
+
// RU: 'Russia',
|
|
9507
|
+
// };
|
|
9476
9508
|
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
return 'unknown';
|
|
9480
|
-
}
|
|
9481
|
-
|
|
9482
|
-
return processNationality(country);
|
|
9483
|
-
}
|
|
9509
|
+
// return countryNames[code] || code;
|
|
9510
|
+
// }
|
|
9484
9511
|
|
|
9485
|
-
|
|
9486
|
-
|
|
9512
|
+
// function getPronounsFromGender(gender) {
|
|
9513
|
+
// switch (gender) {
|
|
9514
|
+
// case 'male':
|
|
9515
|
+
// return ['he/him', 'his'];
|
|
9516
|
+
// case 'female':
|
|
9517
|
+
// return ['she/her', 'hers'];
|
|
9518
|
+
// case 'non-binary':
|
|
9519
|
+
// return ['they/them', 'theirs'];
|
|
9520
|
+
// default:
|
|
9521
|
+
// return ['unknown'];
|
|
9522
|
+
// }
|
|
9523
|
+
// }
|
|
9487
9524
|
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
GB: 'United Kingdom',
|
|
9491
|
-
CA: 'Canada',
|
|
9492
|
-
AU: 'Australia',
|
|
9493
|
-
DE: 'Germany',
|
|
9494
|
-
FR: 'France',
|
|
9495
|
-
JP: 'Japan',
|
|
9496
|
-
CN: 'China',
|
|
9497
|
-
IN: 'India',
|
|
9498
|
-
BR: 'Brazil',
|
|
9499
|
-
MX: 'Mexico',
|
|
9500
|
-
ES: 'Spain',
|
|
9501
|
-
IT: 'Italy',
|
|
9502
|
-
NL: 'Netherlands',
|
|
9503
|
-
SE: 'Sweden',
|
|
9504
|
-
CH: 'Switzerland',
|
|
9505
|
-
KR: 'South Korea',
|
|
9506
|
-
RU: 'Russia',
|
|
9507
|
-
};
|
|
9525
|
+
// function extractContextInsights(emailContext) {
|
|
9526
|
+
// if (!emailContext) return [];
|
|
9508
9527
|
|
|
9509
|
-
|
|
9510
|
-
|
|
9528
|
+
// const insights = [];
|
|
9529
|
+
// const context = emailContext.toLowerCase();
|
|
9511
9530
|
|
|
9512
|
-
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
default:
|
|
9521
|
-
return ['unknown'];
|
|
9522
|
-
}
|
|
9523
|
-
}
|
|
9531
|
+
// // Extract potential job role hints
|
|
9532
|
+
// const rolePatterns = {
|
|
9533
|
+
// manager: ['manage', 'supervise', 'team lead', 'department head'],
|
|
9534
|
+
// engineer: ['engineer', 'developer', 'programmer', 'software'],
|
|
9535
|
+
// sales: ['sales', 'account executive', 'business development'],
|
|
9536
|
+
// marketing: ['marketing', 'campaign', 'brand', 'social media'],
|
|
9537
|
+
// executive: ['ceo', 'cto', 'cfo', 'director', 'vp', 'vice president'],
|
|
9538
|
+
// };
|
|
9524
9539
|
|
|
9525
|
-
|
|
9526
|
-
|
|
9540
|
+
// for (const [role, patterns] of Object.entries(rolePatterns)) {
|
|
9541
|
+
// if (patterns.some((pattern) => context.includes(pattern))) {
|
|
9542
|
+
// insights.push(`Possible ${role} role indicated in email`);
|
|
9543
|
+
// }
|
|
9544
|
+
// }
|
|
9527
9545
|
|
|
9528
|
-
|
|
9529
|
-
|
|
9546
|
+
// // Extract company hints
|
|
9547
|
+
// const companyMatch = context.match(/(?:at|from|of)\s+([A-Z][A-Za-z0-9\s&]+)(?:\s|$)/);
|
|
9548
|
+
// if (companyMatch && companyMatch[1].length > 2) {
|
|
9549
|
+
// insights.push(`Mentioned company/organization: ${companyMatch[1].trim()}`);
|
|
9550
|
+
// }
|
|
9530
9551
|
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
marketing: ['marketing', 'campaign', 'brand', 'social media'],
|
|
9537
|
-
executive: ['ceo', 'cto', 'cfo', 'director', 'vp', 'vice president'],
|
|
9538
|
-
};
|
|
9552
|
+
// // Extract project/technology hints
|
|
9553
|
+
// const techMatch = context.match(/(?:using|with|built\s+in)\s+([A-Za-z0-9\s+#]+)(?:\s|$)/);
|
|
9554
|
+
// if (techMatch) {
|
|
9555
|
+
// insights.push(`Technology mentioned: ${techMatch[1].trim()}`);
|
|
9556
|
+
// }
|
|
9539
9557
|
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
}
|
|
9558
|
+
// // Extract urgency/priority
|
|
9559
|
+
// if (context.includes('urgent') || context.includes('asap') || context.includes('immediately')) {
|
|
9560
|
+
// insights.push('Email suggests urgency or time sensitivity');
|
|
9561
|
+
// }
|
|
9545
9562
|
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
}
|
|
9563
|
+
// // Extract tone
|
|
9564
|
+
// if (context.includes('thank you') || context.includes('appreciate') || context.includes('grateful')) {
|
|
9565
|
+
// insights.push('Email shows appreciative/grateful tone');
|
|
9566
|
+
// }
|
|
9551
9567
|
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
if (techMatch) {
|
|
9555
|
-
insights.push(`Technology mentioned: ${techMatch[1].trim()}`);
|
|
9556
|
-
}
|
|
9568
|
+
// return insights.slice(0, 5); // Limit to 5 insights
|
|
9569
|
+
// }
|
|
9557
9570
|
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9571
|
+
// // Existing helper functions (kept from previous version)
|
|
9572
|
+
// function validateURL(url) {
|
|
9573
|
+
// /* ... same as before ... */
|
|
9574
|
+
// }
|
|
9575
|
+
// function validatePhoneNumber(phone) {
|
|
9576
|
+
// /* ... same as before ... */
|
|
9577
|
+
// }
|
|
9578
|
+
// function cleanText(text) {
|
|
9579
|
+
// /* ... same as before ... */
|
|
9580
|
+
// }
|
|
9581
|
+
// function cleanStateProvince(state) {
|
|
9582
|
+
// /* ... same as before ... */
|
|
9583
|
+
// }
|
|
9584
|
+
// function ensureArray(value) {
|
|
9585
|
+
// /* ... same as before ... */
|
|
9586
|
+
// }
|
|
9587
|
+
// function getInitials(firstName, lastName) {
|
|
9588
|
+
// /* ... same as before ... */
|
|
9589
|
+
// }
|
|
9590
|
+
// function calculateSeniorityScore(data) {
|
|
9591
|
+
// /* ... same as before ... */
|
|
9592
|
+
// }
|
|
9562
9593
|
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9594
|
+
// function getDefaultPersonInfo(name, email, emailContext = '', error = null) {
|
|
9595
|
+
// const nameParts = name.trim().split(/\s+/);
|
|
9596
|
+
// const firstName = nameParts[0] || '';
|
|
9597
|
+
// const lastName = nameParts.slice(1).join(' ') || '';
|
|
9598
|
+
// const domain = email.includes('@') ? email.split('@')[1] : '';
|
|
9599
|
+
// const companyGuess = domain
|
|
9600
|
+
// .replace(/\..*$/, '')
|
|
9601
|
+
// .replace(/[^a-z]/gi, ' ')
|
|
9602
|
+
// .replace(/\b\w/g, (l) => l.toUpperCase());
|
|
9567
9603
|
|
|
9568
|
-
|
|
9569
|
-
|
|
9604
|
+
// // Extract basic insights from email context
|
|
9605
|
+
// const contextInsights = emailContext ? extractContextInsights(emailContext) : [];
|
|
9570
9606
|
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9607
|
+
// return {
|
|
9608
|
+
// // Basic Information
|
|
9609
|
+
// person_full_name: name,
|
|
9610
|
+
// person_first_name: firstName,
|
|
9611
|
+
// person_last_name: lastName,
|
|
9612
|
+
// person_middle_name: '',
|
|
9613
|
+
// person_preferred_name: '',
|
|
9614
|
+
|
|
9615
|
+
// // Demographic Information
|
|
9616
|
+
// person_age: 'unknown',
|
|
9617
|
+
// person_gender: 'unknown',
|
|
9618
|
+
// person_nationality: 'unknown',
|
|
9619
|
+
// person_age_range: 'Unknown',
|
|
9620
|
+
// person_pronouns: ['unknown'],
|
|
9621
|
+
|
|
9622
|
+
// // Professional Information
|
|
9623
|
+
// person_title: 'Professional',
|
|
9624
|
+
// person_company: companyGuess || 'Unknown',
|
|
9625
|
+
// person_industry: 'Technology',
|
|
9626
|
+
// person_bio: 'Information not available',
|
|
9627
|
+
|
|
9628
|
+
// // Location
|
|
9629
|
+
// person_location_city: 'Not available',
|
|
9630
|
+
// person_location_state: 'Not available',
|
|
9631
|
+
// person_location_country: 'unknown',
|
|
9632
|
+
// person_location_full: 'Not available',
|
|
9633
|
+
|
|
9634
|
+
// // Contact Information
|
|
9635
|
+
// person_phone: 'Not available',
|
|
9636
|
+
// person_email: email,
|
|
9637
|
+
// person_email_alternate: '',
|
|
9638
|
+
|
|
9639
|
+
// // Education
|
|
9640
|
+
// person_education: [],
|
|
9641
|
+
// person_education_highest: 'Not available',
|
|
9642
|
+
|
|
9643
|
+
// // Professional Details
|
|
9644
|
+
// person_skills: [],
|
|
9645
|
+
// person_expertise: [],
|
|
9646
|
+
// person_career_level: 'unknown',
|
|
9647
|
+
// person_years_experience: 0,
|
|
9648
|
+
|
|
9649
|
+
// // Social & Online Presence
|
|
9650
|
+
// person_linkedin: 'Not available',
|
|
9651
|
+
// person_twitter: 'Not available',
|
|
9652
|
+
// person_github: 'Not available',
|
|
9653
|
+
// person_website: 'Not available',
|
|
9654
|
+
// person_social_other: [],
|
|
9655
|
+
|
|
9656
|
+
// // Additional Information
|
|
9657
|
+
// person_languages: ['English'],
|
|
9658
|
+
// person_interests: [],
|
|
9659
|
+
// person_achievements: [],
|
|
9660
|
+
// person_current_projects: [],
|
|
9661
|
+
// person_context_insights: contextInsights,
|
|
9593
9662
|
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
const companyGuess = domain
|
|
9600
|
-
.replace(/\..*$/, '')
|
|
9601
|
-
.replace(/[^a-z]/gi, ' ')
|
|
9602
|
-
.replace(/\b\w/g, (l) => l.toUpperCase());
|
|
9663
|
+
// // Metadata
|
|
9664
|
+
// person_available_for_opportunities: false,
|
|
9665
|
+
// person_last_updated: new Date().toISOString(),
|
|
9666
|
+
// person_source_confidence: 0,
|
|
9667
|
+
// person_has_context_hints: contextInsights.length > 0,
|
|
9603
9668
|
|
|
9604
|
-
|
|
9605
|
-
|
|
9669
|
+
// // Processed fields
|
|
9670
|
+
// person_name_initials: getInitials(firstName, lastName),
|
|
9671
|
+
// person_seniority_score: 0,
|
|
9606
9672
|
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9673
|
+
// // Structured objects
|
|
9674
|
+
// person_demographics: {
|
|
9675
|
+
// age: 'unknown',
|
|
9676
|
+
// age_range: 'Unknown',
|
|
9677
|
+
// gender: 'unknown',
|
|
9678
|
+
// pronouns: ['unknown'],
|
|
9679
|
+
// nationality: 'unknown',
|
|
9680
|
+
// nationality_name: 'Unknown',
|
|
9681
|
+
// },
|
|
9614
9682
|
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9683
|
+
// person_contact: {
|
|
9684
|
+
// email: email,
|
|
9685
|
+
// phone: 'Not available',
|
|
9686
|
+
// location: {
|
|
9687
|
+
// city: 'Not available',
|
|
9688
|
+
// state: 'Not available',
|
|
9689
|
+
// country: 'unknown',
|
|
9690
|
+
// country_name: 'Unknown',
|
|
9691
|
+
// full: 'Not available',
|
|
9692
|
+
// },
|
|
9693
|
+
// },
|
|
9621
9694
|
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9695
|
+
// person_professional: {
|
|
9696
|
+
// title: 'Professional',
|
|
9697
|
+
// company: companyGuess || 'Unknown',
|
|
9698
|
+
// industry: 'Technology',
|
|
9699
|
+
// career_level: 'unknown',
|
|
9700
|
+
// years_experience: 0,
|
|
9701
|
+
// skills: [],
|
|
9702
|
+
// expertise: [],
|
|
9703
|
+
// education: [],
|
|
9704
|
+
// },
|
|
9627
9705
|
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9706
|
+
// person_online: {
|
|
9707
|
+
// linkedin: 'Not available',
|
|
9708
|
+
// twitter: 'Not available',
|
|
9709
|
+
// github: 'Not available',
|
|
9710
|
+
// website: 'Not available',
|
|
9711
|
+
// other: [],
|
|
9712
|
+
// },
|
|
9633
9713
|
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9714
|
+
// context_analysis: {
|
|
9715
|
+
// has_context: !!emailContext,
|
|
9716
|
+
// context_length: emailContext?.length || 0,
|
|
9717
|
+
// extracted_insights: contextInsights,
|
|
9718
|
+
// context_useful: contextInsights.length > 0,
|
|
9719
|
+
// },
|
|
9638
9720
|
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9721
|
+
// retrieved_at: new Date().toISOString(),
|
|
9722
|
+
// error: error || 'API call failed',
|
|
9723
|
+
// };
|
|
9724
|
+
// }
|
|
9642
9725
|
|
|
9643
|
-
|
|
9644
|
-
|
|
9645
|
-
person_expertise: [],
|
|
9646
|
-
person_career_level: 'unknown',
|
|
9647
|
-
person_years_experience: 0,
|
|
9726
|
+
// // Build context-aware prompt
|
|
9727
|
+
// let context_prompt = `Research the person: "${name}" (${email}).`;
|
|
9648
9728
|
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
person_github: 'Not available',
|
|
9653
|
-
person_website: 'Not available',
|
|
9654
|
-
person_social_other: [],
|
|
9729
|
+
// if (email_context) {
|
|
9730
|
+
// context_prompt += `\n\nEMAIL CONTEXT PROVIDED:\n"${email_context.substring(0, 500)}${email_context.length > 500 ? '...' : ''}"`;
|
|
9731
|
+
// }
|
|
9655
9732
|
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9733
|
+
// context_prompt += `
|
|
9734
|
+
|
|
9735
|
+
// REQUIRED PERSON INFORMATION:
|
|
9736
|
+
// 1. Full name (first, middle, last)
|
|
9737
|
+
// 2. Professional title/role
|
|
9738
|
+
// 3. Company/organization they work for
|
|
9739
|
+
// 4. Industry/field they work in
|
|
9740
|
+
// 5. Location (city, state, country)
|
|
9741
|
+
// 6. Professional biography (1-2 sentences)
|
|
9742
|
+
// 7. Education background
|
|
9743
|
+
// 8. Professional skills/expertise
|
|
9744
|
+
// 9. Social media profiles (LinkedIn, Twitter, GitHub, etc.)
|
|
9745
|
+
// 10. Personal website or portfolio
|
|
9746
|
+
|
|
9747
|
+
// DEMOGRAPHIC INFORMATION:
|
|
9748
|
+
// 11. Estimated age (based on career, education, and public information)
|
|
9749
|
+
// 12. Gender (based on name, pronouns in content, or public profiles)
|
|
9750
|
+
// 13. Nationality/country of origin (if discernible) (based on name, pronouns in content, or public profiles)
|
|
9751
|
+
|
|
9752
|
+
// CONTACT INFORMATION:
|
|
9753
|
+
// 14. Phone number (if available)
|
|
9754
|
+
// 15. Alternate email addresses (if available)
|
|
9755
|
+
// 16. Physical address (if available and appropriate)
|
|
9756
|
+
|
|
9757
|
+
// ADDITIONAL INSIGHTS:
|
|
9758
|
+
// 17. Career level (entry, mid, senior, executive, etc.)
|
|
9759
|
+
// 18. Years of experience
|
|
9760
|
+
// 19. Notable achievements/awards
|
|
9761
|
+
// 20. Languages spoken
|
|
9762
|
+
// 21. Professional interests
|
|
9763
|
+
// 22. Current projects
|
|
9764
|
+
// 23. Availability for opportunities
|
|
9662
9765
|
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9766
|
+
// INSTRUCTIONS:
|
|
9767
|
+
// - Use web search to find accurate, public information
|
|
9768
|
+
// - Use email context provided to infer additional details
|
|
9769
|
+
// - Respect privacy - only include publicly available information
|
|
9770
|
+
// - For age: provide numeric estimate or range (e.g., 35, 30-40, unknown)
|
|
9771
|
+
// - For gender: use male/female/non-binary/unknown based on available cues
|
|
9772
|
+
// - For country: use 2-letter ISO code (e.g., US, IN, UK)
|
|
9773
|
+
// - If information is not available, use "Not available" or appropriate defaults
|
|
9774
|
+
// - For location, use standard formats (e.g., "San Francisco, CA, USA")
|
|
9775
|
+
// - For social media, provide full URLs when available
|
|
9776
|
+
// - Focus on professional information suitable for business networking
|
|
9777
|
+
|
|
9778
|
+
// Be thorough but respectful of privacy boundaries.`;
|
|
9779
|
+
|
|
9780
|
+
// const person_info_ret = await submit_chat_gpt_prompt({
|
|
9781
|
+
// uid,
|
|
9782
|
+
// prompt: context_prompt,
|
|
9783
|
+
// model: 'gpt-5-nano',
|
|
9784
|
+
// response_format: z.object({
|
|
9785
|
+
// // Basic Information
|
|
9786
|
+
// person_full_name: z.string().describe("Person's full name"),
|
|
9787
|
+
// person_first_name: z.string().describe('First name'),
|
|
9788
|
+
// person_last_name: z.string().describe('Last name'),
|
|
9789
|
+
// person_middle_name: z.string().optional().nullable().describe('Middle name if available'),
|
|
9790
|
+
// person_preferred_name: z.string().optional().nullable().describe('Preferred/nickname if different'),
|
|
9791
|
+
|
|
9792
|
+
// // Demographic Information
|
|
9793
|
+
// person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('find age base on the name Estimated age or range'),
|
|
9794
|
+
// person_gender: z.enum(['male', 'female']).describe('find Gender based on the name'),
|
|
9795
|
+
// person_nationality: z.string().describe('find nationality based on the name Country of origin/nationality (2-letter ISO code)'),
|
|
9796
|
+
|
|
9797
|
+
// // Professional Information
|
|
9798
|
+
// person_title: z.string().describe('Professional title/role'),
|
|
9799
|
+
// person_company: z.string().describe('Current company/organization'),
|
|
9800
|
+
// person_industry: z.string().describe('Primary industry/field'),
|
|
9801
|
+
// person_bio: z.string().describe('Professional biography (1-2 sentences)'),
|
|
9802
|
+
|
|
9803
|
+
// // Location
|
|
9804
|
+
// person_location_city: z.string().describe('City of residence/work'),
|
|
9805
|
+
// person_location_state: z.string().describe('State/province'),
|
|
9806
|
+
// person_location_country: z.string().describe('Country (2-letter ISO code)'),
|
|
9807
|
+
// person_location_full: z.string().describe('Full location string'),
|
|
9808
|
+
|
|
9809
|
+
// // Contact Information
|
|
9810
|
+
// person_phone: z.string().describe("Phone number or 'Not available'"),
|
|
9811
|
+
// person_email: z.string().describe('Primary email address'),
|
|
9812
|
+
// person_email_alternate: z.string().optional().nullable().describe('Alternate email if available'),
|
|
9813
|
+
|
|
9814
|
+
// // Education
|
|
9815
|
+
// person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
|
|
9816
|
+
// person_education_highest: z.string().describe('Highest degree obtained'),
|
|
9817
|
+
|
|
9818
|
+
// // Professional Details
|
|
9819
|
+
// person_skills: z.array(z.string()).describe('Array of professional skills'),
|
|
9820
|
+
// person_expertise: z.array(z.string()).describe('Areas of expertise'),
|
|
9821
|
+
// person_career_level: z.enum(['entry', 'mid', 'senior', 'lead', 'manager', 'director', 'executive', 'founder', 'unknown']),
|
|
9822
|
+
// person_years_experience: z.number().int().min(0).max(60).describe('Years of professional experience'),
|
|
9823
|
+
|
|
9824
|
+
// // Social & Online Presence
|
|
9825
|
+
// person_linkedin: z.string().describe("LinkedIn profile URL or 'Not available'"),
|
|
9826
|
+
// person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
|
|
9827
|
+
// person_github: z.string().describe("GitHub profile URL or 'Not available'"),
|
|
9828
|
+
// person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
|
|
9829
|
+
// person_social_other: z.array(z.string()).optional().nullable().describe('Other social media profiles'),
|
|
9830
|
+
|
|
9831
|
+
// // Additional Information
|
|
9832
|
+
// person_languages: z.array(z.string()).describe('Languages spoken'),
|
|
9833
|
+
// person_interests: z.array(z.string()).describe('Professional/personal interests'),
|
|
9834
|
+
// person_achievements: z.array(z.string()).optional().nullable().describe('Notable achievements/awards'),
|
|
9835
|
+
// person_current_projects: z.array(z.string()).optional().nullable().describe('Current projects'),
|
|
9836
|
+
|
|
9837
|
+
// // Context Extracted Information
|
|
9838
|
+
// person_context_insights: z.array(z.string()).optional().nullable().describe('Insights extracted from provided email context'),
|
|
9668
9839
|
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9840
|
+
// // Metadata
|
|
9841
|
+
// person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
|
|
9842
|
+
// person_last_updated: z.string().describe('Date information was last verified'),
|
|
9843
|
+
// person_source_confidence: z.number().min(0).max(100).describe('Confidence score for information accuracy'),
|
|
9844
|
+
// person_has_context_hints: z.boolean().describe('Whether email context provided useful hints'),
|
|
9845
|
+
// }),
|
|
9846
|
+
// metadata: {
|
|
9847
|
+
// func: 'get_person_info',
|
|
9848
|
+
// person_name: name,
|
|
9849
|
+
// person_email: email,
|
|
9850
|
+
// has_email_context: !!email_context,
|
|
9851
|
+
// email_context_length: email_context?.length || 0,
|
|
9852
|
+
// context_hash: email_context ? await hashString(email_context.substring(0, 200)) : null,
|
|
9853
|
+
// },
|
|
9854
|
+
// account_profile_info,
|
|
9855
|
+
// tools: [{ type: 'web_search_preview' }],
|
|
9856
|
+
// });
|
|
9672
9857
|
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
age_range: 'Unknown',
|
|
9677
|
-
gender: 'unknown',
|
|
9678
|
-
pronouns: ['unknown'],
|
|
9679
|
-
nationality: 'unknown',
|
|
9680
|
-
nationality_name: 'Unknown',
|
|
9681
|
-
},
|
|
9858
|
+
// try {
|
|
9859
|
+
// if (person_info_ret.code > -1) {
|
|
9860
|
+
// const data = typeof person_info_ret.data === 'string' ? JSON.parse(person_info_ret.data) : person_info_ret.data;
|
|
9682
9861
|
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
phone: 'Not available',
|
|
9686
|
-
location: {
|
|
9687
|
-
city: 'Not available',
|
|
9688
|
-
state: 'Not available',
|
|
9689
|
-
country: 'unknown',
|
|
9690
|
-
country_name: 'Unknown',
|
|
9691
|
-
full: 'Not available',
|
|
9692
|
-
},
|
|
9693
|
-
},
|
|
9862
|
+
// // Process and enhance the data
|
|
9863
|
+
// const processedData = processPersonInfo(data, name, email, email_context);
|
|
9694
9864
|
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9865
|
+
// return processedData;
|
|
9866
|
+
// } else {
|
|
9867
|
+
// console.error('Person info API call failed:', person_info_ret);
|
|
9868
|
+
// return getDefaultPersonInfo(name, email, email_context);
|
|
9869
|
+
// }
|
|
9870
|
+
// } catch (error) {
|
|
9871
|
+
// console.error('Error in get_person_info:', error);
|
|
9872
|
+
// return getDefaultPersonInfo(name, email, email_context, error.message);
|
|
9873
|
+
// }
|
|
9874
|
+
// };
|
|
9705
9875
|
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
twitter: 'Not available',
|
|
9709
|
-
github: 'Not available',
|
|
9710
|
-
website: 'Not available',
|
|
9711
|
-
other: [],
|
|
9712
|
-
},
|
|
9713
|
-
|
|
9714
|
-
context_analysis: {
|
|
9715
|
-
has_context: !!emailContext,
|
|
9716
|
-
context_length: emailContext?.length || 0,
|
|
9717
|
-
extracted_insights: contextInsights,
|
|
9718
|
-
context_useful: contextInsights.length > 0,
|
|
9719
|
-
},
|
|
9720
|
-
|
|
9721
|
-
retrieved_at: new Date().toISOString(),
|
|
9722
|
-
error: error || 'API call failed',
|
|
9723
|
-
};
|
|
9724
|
-
}
|
|
9725
|
-
|
|
9726
|
-
// Build context-aware prompt
|
|
9727
|
-
let context_prompt = `Research the person: "${name}" (${email}).`;
|
|
9728
|
-
|
|
9729
|
-
if (email_context) {
|
|
9730
|
-
context_prompt += `\n\nEMAIL CONTEXT PROVIDED:\n"${email_context.substring(0, 500)}${email_context.length > 500 ? '...' : ''}"`;
|
|
9731
|
-
}
|
|
9732
|
-
|
|
9733
|
-
context_prompt += `
|
|
9734
|
-
|
|
9735
|
-
REQUIRED PERSON INFORMATION:
|
|
9736
|
-
1. Full name (first, middle, last)
|
|
9737
|
-
2. Professional title/role
|
|
9738
|
-
3. Company/organization they work for
|
|
9739
|
-
4. Industry/field they work in
|
|
9740
|
-
5. Location (city, state, country)
|
|
9741
|
-
6. Professional biography (1-2 sentences)
|
|
9742
|
-
7. Education background
|
|
9743
|
-
8. Professional skills/expertise
|
|
9744
|
-
9. Social media profiles (LinkedIn, Twitter, GitHub, etc.)
|
|
9745
|
-
10. Personal website or portfolio
|
|
9746
|
-
|
|
9747
|
-
DEMOGRAPHIC INFORMATION:
|
|
9748
|
-
11. Estimated age (based on career, education, and public information)
|
|
9749
|
-
12. Gender (based on name, pronouns in content, or public profiles)
|
|
9750
|
-
13. Nationality/country of origin (if discernible) (based on name, pronouns in content, or public profiles)
|
|
9751
|
-
|
|
9752
|
-
CONTACT INFORMATION:
|
|
9753
|
-
14. Phone number (if available)
|
|
9754
|
-
15. Alternate email addresses (if available)
|
|
9755
|
-
16. Physical address (if available and appropriate)
|
|
9756
|
-
|
|
9757
|
-
ADDITIONAL INSIGHTS:
|
|
9758
|
-
17. Career level (entry, mid, senior, executive, etc.)
|
|
9759
|
-
18. Years of experience
|
|
9760
|
-
19. Notable achievements/awards
|
|
9761
|
-
20. Languages spoken
|
|
9762
|
-
21. Professional interests
|
|
9763
|
-
22. Current projects
|
|
9764
|
-
23. Availability for opportunities
|
|
9765
|
-
|
|
9766
|
-
INSTRUCTIONS:
|
|
9767
|
-
- Use web search to find accurate, public information
|
|
9768
|
-
- Use email context provided to infer additional details
|
|
9769
|
-
- Respect privacy - only include publicly available information
|
|
9770
|
-
- For age: provide numeric estimate or range (e.g., 35, 30-40, unknown)
|
|
9771
|
-
- For gender: use male/female/non-binary/unknown based on available cues
|
|
9772
|
-
- For country: use 2-letter ISO code (e.g., US, IN, UK)
|
|
9773
|
-
- If information is not available, use "Not available" or appropriate defaults
|
|
9774
|
-
- For location, use standard formats (e.g., "San Francisco, CA, USA")
|
|
9775
|
-
- For social media, provide full URLs when available
|
|
9776
|
-
- Focus on professional information suitable for business networking
|
|
9777
|
-
|
|
9778
|
-
Be thorough but respectful of privacy boundaries.`;
|
|
9779
|
-
|
|
9780
|
-
const person_info_ret = await submit_chat_gpt_prompt({
|
|
9781
|
-
uid,
|
|
9782
|
-
prompt: context_prompt,
|
|
9783
|
-
model: 'gpt-5-nano',
|
|
9784
|
-
response_format: z.object({
|
|
9785
|
-
// Basic Information
|
|
9786
|
-
person_full_name: z.string().describe("Person's full name"),
|
|
9787
|
-
person_first_name: z.string().describe('First name'),
|
|
9788
|
-
person_last_name: z.string().describe('Last name'),
|
|
9789
|
-
person_middle_name: z.string().optional().nullable().describe('Middle name if available'),
|
|
9790
|
-
person_preferred_name: z.string().optional().nullable().describe('Preferred/nickname if different'),
|
|
9791
|
-
|
|
9792
|
-
// Demographic Information
|
|
9793
|
-
person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('find age base on the name Estimated age or range'),
|
|
9794
|
-
person_gender: z.enum(['male', 'female']).describe('find Gender based on the name'),
|
|
9795
|
-
person_nationality: z.string().describe('find nationality based on the name Country of origin/nationality (2-letter ISO code)'),
|
|
9796
|
-
|
|
9797
|
-
// Professional Information
|
|
9798
|
-
person_title: z.string().describe('Professional title/role'),
|
|
9799
|
-
person_company: z.string().describe('Current company/organization'),
|
|
9800
|
-
person_industry: z.string().describe('Primary industry/field'),
|
|
9801
|
-
person_bio: z.string().describe('Professional biography (1-2 sentences)'),
|
|
9802
|
-
|
|
9803
|
-
// Location
|
|
9804
|
-
person_location_city: z.string().describe('City of residence/work'),
|
|
9805
|
-
person_location_state: z.string().describe('State/province'),
|
|
9806
|
-
person_location_country: z.string().describe('Country (2-letter ISO code)'),
|
|
9807
|
-
person_location_full: z.string().describe('Full location string'),
|
|
9808
|
-
|
|
9809
|
-
// Contact Information
|
|
9810
|
-
person_phone: z.string().describe("Phone number or 'Not available'"),
|
|
9811
|
-
person_email: z.string().describe('Primary email address'),
|
|
9812
|
-
person_email_alternate: z.string().optional().nullable().describe('Alternate email if available'),
|
|
9813
|
-
|
|
9814
|
-
// Education
|
|
9815
|
-
person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
|
|
9816
|
-
person_education_highest: z.string().describe('Highest degree obtained'),
|
|
9817
|
-
|
|
9818
|
-
// Professional Details
|
|
9819
|
-
person_skills: z.array(z.string()).describe('Array of professional skills'),
|
|
9820
|
-
person_expertise: z.array(z.string()).describe('Areas of expertise'),
|
|
9821
|
-
person_career_level: z.enum(['entry', 'mid', 'senior', 'lead', 'manager', 'director', 'executive', 'founder', 'unknown']),
|
|
9822
|
-
person_years_experience: z.number().int().min(0).max(60).describe('Years of professional experience'),
|
|
9823
|
-
|
|
9824
|
-
// Social & Online Presence
|
|
9825
|
-
person_linkedin: z.string().describe("LinkedIn profile URL or 'Not available'"),
|
|
9826
|
-
person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
|
|
9827
|
-
person_github: z.string().describe("GitHub profile URL or 'Not available'"),
|
|
9828
|
-
person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
|
|
9829
|
-
person_social_other: z.array(z.string()).optional().nullable().describe('Other social media profiles'),
|
|
9830
|
-
|
|
9831
|
-
// Additional Information
|
|
9832
|
-
person_languages: z.array(z.string()).describe('Languages spoken'),
|
|
9833
|
-
person_interests: z.array(z.string()).describe('Professional/personal interests'),
|
|
9834
|
-
person_achievements: z.array(z.string()).optional().nullable().describe('Notable achievements/awards'),
|
|
9835
|
-
person_current_projects: z.array(z.string()).optional().nullable().describe('Current projects'),
|
|
9836
|
-
|
|
9837
|
-
// Context Extracted Information
|
|
9838
|
-
person_context_insights: z.array(z.string()).optional().nullable().describe('Insights extracted from provided email context'),
|
|
9839
|
-
|
|
9840
|
-
// Metadata
|
|
9841
|
-
person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
|
|
9842
|
-
person_last_updated: z.string().describe('Date information was last verified'),
|
|
9843
|
-
person_source_confidence: z.number().min(0).max(100).describe('Confidence score for information accuracy'),
|
|
9844
|
-
person_has_context_hints: z.boolean().describe('Whether email context provided useful hints'),
|
|
9845
|
-
}),
|
|
9846
|
-
metadata: {
|
|
9847
|
-
func: 'get_person_info',
|
|
9848
|
-
person_name: name,
|
|
9849
|
-
person_email: email,
|
|
9850
|
-
has_email_context: !!email_context,
|
|
9851
|
-
email_context_length: email_context?.length || 0,
|
|
9852
|
-
context_hash: email_context ? await hashString(email_context.substring(0, 200)) : null,
|
|
9853
|
-
},
|
|
9854
|
-
account_profile_info,
|
|
9855
|
-
tools: [{ type: 'web_search_preview' }],
|
|
9856
|
-
});
|
|
9857
|
-
|
|
9858
|
-
try {
|
|
9859
|
-
if (person_info_ret.code > -1) {
|
|
9860
|
-
const data = typeof person_info_ret.data === 'string' ? JSON.parse(person_info_ret.data) : person_info_ret.data;
|
|
9861
|
-
|
|
9862
|
-
// Process and enhance the data
|
|
9863
|
-
const processedData = processPersonInfo(data, name, email, email_context);
|
|
9864
|
-
|
|
9865
|
-
return processedData;
|
|
9866
|
-
} else {
|
|
9867
|
-
console.error('Person info API call failed:', person_info_ret);
|
|
9868
|
-
return getDefaultPersonInfo(name, email, email_context);
|
|
9869
|
-
}
|
|
9870
|
-
} catch (error) {
|
|
9871
|
-
console.error('Error in get_person_info:', error);
|
|
9872
|
-
return getDefaultPersonInfo(name, email, email_context, error.message);
|
|
9873
|
-
}
|
|
9874
|
-
};
|
|
9875
|
-
|
|
9876
|
-
// export const analyze_email_account_type = async function (uid, name, email, account_profile_info, email_context = '') {
|
|
9877
|
-
// // ========== HELPER FUNCTIONS ==========
|
|
9876
|
+
// export const analyze_email_account_type = async function (uid, name, email, account_profile_info, email_context = '') {
|
|
9877
|
+
// // ========== HELPER FUNCTIONS ==========
|
|
9878
9878
|
|
|
9879
9879
|
// function enhanceEmailAnalysis(data, name, email, emailContext) {
|
|
9880
9880
|
// const domain = email.split('@')[1] || '';
|
|
@@ -11911,3 +11911,2149 @@ export const get_business_info = async function (uid, name, email, account_profi
|
|
|
11911
11911
|
return getDefaultBusinessInfo(name, email, error.message);
|
|
11912
11912
|
}
|
|
11913
11913
|
};
|
|
11914
|
+
|
|
11915
|
+
export const get_person_info = async function (uid, name, email, account_profile_info, email_context = '', options = {}) {
|
|
11916
|
+
// ========== HELPER FUNCTIONS ==========
|
|
11917
|
+
|
|
11918
|
+
function extractNameParts(fullName) {
|
|
11919
|
+
const nameParts = fullName.trim().split(/\s+/);
|
|
11920
|
+
|
|
11921
|
+
if (nameParts.length === 1) {
|
|
11922
|
+
return {
|
|
11923
|
+
firstName: nameParts[0],
|
|
11924
|
+
lastName: '',
|
|
11925
|
+
middleName: '',
|
|
11926
|
+
};
|
|
11927
|
+
}
|
|
11928
|
+
|
|
11929
|
+
if (nameParts.length === 2) {
|
|
11930
|
+
return {
|
|
11931
|
+
firstName: nameParts[0],
|
|
11932
|
+
lastName: nameParts[1],
|
|
11933
|
+
middleName: '',
|
|
11934
|
+
};
|
|
11935
|
+
}
|
|
11936
|
+
|
|
11937
|
+
// For names with 3+ parts, assume first is first name, last is last name, middle is middle name
|
|
11938
|
+
return {
|
|
11939
|
+
firstName: nameParts[0],
|
|
11940
|
+
lastName: nameParts[nameParts.length - 1],
|
|
11941
|
+
middleName: nameParts.slice(1, nameParts.length - 1).join(' '),
|
|
11942
|
+
};
|
|
11943
|
+
}
|
|
11944
|
+
|
|
11945
|
+
function inferGenderFromName(firstName) {
|
|
11946
|
+
if (!firstName) return 'unknown';
|
|
11947
|
+
|
|
11948
|
+
const firstNameLower = firstName.toLowerCase();
|
|
11949
|
+
|
|
11950
|
+
// Common male first names
|
|
11951
|
+
const maleNames = [
|
|
11952
|
+
'john',
|
|
11953
|
+
'james',
|
|
11954
|
+
'robert',
|
|
11955
|
+
'michael',
|
|
11956
|
+
'william',
|
|
11957
|
+
'david',
|
|
11958
|
+
'richard',
|
|
11959
|
+
'charles',
|
|
11960
|
+
'joseph',
|
|
11961
|
+
'thomas',
|
|
11962
|
+
'chris',
|
|
11963
|
+
'daniel',
|
|
11964
|
+
'paul',
|
|
11965
|
+
'mark',
|
|
11966
|
+
'donald',
|
|
11967
|
+
'george',
|
|
11968
|
+
'kenneth',
|
|
11969
|
+
'steven',
|
|
11970
|
+
'edward',
|
|
11971
|
+
'brian',
|
|
11972
|
+
'ronald',
|
|
11973
|
+
'anthony',
|
|
11974
|
+
'kevin',
|
|
11975
|
+
'jason',
|
|
11976
|
+
'matthew',
|
|
11977
|
+
'gary',
|
|
11978
|
+
'timothy',
|
|
11979
|
+
'jose',
|
|
11980
|
+
'larry',
|
|
11981
|
+
'jeffrey',
|
|
11982
|
+
'frank',
|
|
11983
|
+
'scott',
|
|
11984
|
+
'eric',
|
|
11985
|
+
'stephen',
|
|
11986
|
+
'andrew',
|
|
11987
|
+
'raymond',
|
|
11988
|
+
'gregory',
|
|
11989
|
+
'joshua',
|
|
11990
|
+
'jerry',
|
|
11991
|
+
'dennis',
|
|
11992
|
+
'walter',
|
|
11993
|
+
'patrick',
|
|
11994
|
+
'peter',
|
|
11995
|
+
'harold',
|
|
11996
|
+
'douglas',
|
|
11997
|
+
'henry',
|
|
11998
|
+
'carl',
|
|
11999
|
+
'arthur',
|
|
12000
|
+
'ryan',
|
|
12001
|
+
'roger',
|
|
12002
|
+
'joe',
|
|
12003
|
+
'juan',
|
|
12004
|
+
'jack',
|
|
12005
|
+
'albert',
|
|
12006
|
+
'jonathan',
|
|
12007
|
+
'justin',
|
|
12008
|
+
'terry',
|
|
12009
|
+
'gerald',
|
|
12010
|
+
'keith',
|
|
12011
|
+
'samuel',
|
|
12012
|
+
'willie',
|
|
12013
|
+
'ralph',
|
|
12014
|
+
'lawrence',
|
|
12015
|
+
'nicholas',
|
|
12016
|
+
'roy',
|
|
12017
|
+
'benjamin',
|
|
12018
|
+
'bruce',
|
|
12019
|
+
'brandon',
|
|
12020
|
+
'adam',
|
|
12021
|
+
'harry',
|
|
12022
|
+
'fred',
|
|
12023
|
+
'wayne',
|
|
12024
|
+
'billy',
|
|
12025
|
+
'steve',
|
|
12026
|
+
'louis',
|
|
12027
|
+
'jeremy',
|
|
12028
|
+
'aaron',
|
|
12029
|
+
'randy',
|
|
12030
|
+
'howard',
|
|
12031
|
+
'eugene',
|
|
12032
|
+
'carlos',
|
|
12033
|
+
'russell',
|
|
12034
|
+
'bobby',
|
|
12035
|
+
'victor',
|
|
12036
|
+
'martin',
|
|
12037
|
+
'ernest',
|
|
12038
|
+
'phillip',
|
|
12039
|
+
'todd',
|
|
12040
|
+
'jesse',
|
|
12041
|
+
'craig',
|
|
12042
|
+
'alan',
|
|
12043
|
+
'shawn',
|
|
12044
|
+
'clarence',
|
|
12045
|
+
'sean',
|
|
12046
|
+
'philip',
|
|
12047
|
+
'chris',
|
|
12048
|
+
'johnny',
|
|
12049
|
+
'earl',
|
|
12050
|
+
'jimmy',
|
|
12051
|
+
'antonio',
|
|
12052
|
+
'danny',
|
|
12053
|
+
'bryan',
|
|
12054
|
+
'tony',
|
|
12055
|
+
'luis',
|
|
12056
|
+
'mike',
|
|
12057
|
+
'stanley',
|
|
12058
|
+
'leonard',
|
|
12059
|
+
'nathan',
|
|
12060
|
+
'dale',
|
|
12061
|
+
'manuel',
|
|
12062
|
+
'rodney',
|
|
12063
|
+
'curtis',
|
|
12064
|
+
'norman',
|
|
12065
|
+
'allen',
|
|
12066
|
+
'marvin',
|
|
12067
|
+
'vincent',
|
|
12068
|
+
'glenn',
|
|
12069
|
+
'jeffery',
|
|
12070
|
+
'travis',
|
|
12071
|
+
'jeff',
|
|
12072
|
+
'chad',
|
|
12073
|
+
'jacob',
|
|
12074
|
+
'lee',
|
|
12075
|
+
'melvin',
|
|
12076
|
+
'alfred',
|
|
12077
|
+
'kyle',
|
|
12078
|
+
'francis',
|
|
12079
|
+
'bradley',
|
|
12080
|
+
'jesus',
|
|
12081
|
+
'herbert',
|
|
12082
|
+
'frederick',
|
|
12083
|
+
'ray',
|
|
12084
|
+
'joel',
|
|
12085
|
+
'edwin',
|
|
12086
|
+
'don',
|
|
12087
|
+
'eddie',
|
|
12088
|
+
'ricky',
|
|
12089
|
+
'troy',
|
|
12090
|
+
'randall',
|
|
12091
|
+
'barry',
|
|
12092
|
+
'alexander',
|
|
12093
|
+
'bernard',
|
|
12094
|
+
'mario',
|
|
12095
|
+
'leroy',
|
|
12096
|
+
'francisco',
|
|
12097
|
+
'marcus',
|
|
12098
|
+
'micheal',
|
|
12099
|
+
'theodore',
|
|
12100
|
+
'clifford',
|
|
12101
|
+
'miguel',
|
|
12102
|
+
'oscar',
|
|
12103
|
+
'jay',
|
|
12104
|
+
'jim',
|
|
12105
|
+
'tom',
|
|
12106
|
+
'calvin',
|
|
12107
|
+
'alex',
|
|
12108
|
+
'jon',
|
|
12109
|
+
'ronnie',
|
|
12110
|
+
'bill',
|
|
12111
|
+
'lloyd',
|
|
12112
|
+
'tommy',
|
|
12113
|
+
'leon',
|
|
12114
|
+
'derek',
|
|
12115
|
+
'warren',
|
|
12116
|
+
'darrell',
|
|
12117
|
+
'jerome',
|
|
12118
|
+
'floyd',
|
|
12119
|
+
'leo',
|
|
12120
|
+
'alvin',
|
|
12121
|
+
'tim',
|
|
12122
|
+
'wesley',
|
|
12123
|
+
'gordon',
|
|
12124
|
+
'dean',
|
|
12125
|
+
'greg',
|
|
12126
|
+
'jorge',
|
|
12127
|
+
'dustin',
|
|
12128
|
+
'pedro',
|
|
12129
|
+
'derrick',
|
|
12130
|
+
'dan',
|
|
12131
|
+
'lewis',
|
|
12132
|
+
'zachary',
|
|
12133
|
+
'corey',
|
|
12134
|
+
'herman',
|
|
12135
|
+
'maurice',
|
|
12136
|
+
'vernon',
|
|
12137
|
+
'roberto',
|
|
12138
|
+
'clyde',
|
|
12139
|
+
'glen',
|
|
12140
|
+
'hector',
|
|
12141
|
+
'shane',
|
|
12142
|
+
'ricardo',
|
|
12143
|
+
'sam',
|
|
12144
|
+
'rick',
|
|
12145
|
+
'lester',
|
|
12146
|
+
'brent',
|
|
12147
|
+
'ramon',
|
|
12148
|
+
'charlie',
|
|
12149
|
+
'tyler',
|
|
12150
|
+
'gilbert',
|
|
12151
|
+
'gene',
|
|
12152
|
+
'marc',
|
|
12153
|
+
'reginald',
|
|
12154
|
+
'ruben',
|
|
12155
|
+
'brett',
|
|
12156
|
+
'angel',
|
|
12157
|
+
'nathaniel',
|
|
12158
|
+
'rafael',
|
|
12159
|
+
'leslie',
|
|
12160
|
+
'edgar',
|
|
12161
|
+
'milton',
|
|
12162
|
+
'raul',
|
|
12163
|
+
'ben',
|
|
12164
|
+
'chester',
|
|
12165
|
+
'cecil',
|
|
12166
|
+
'duane',
|
|
12167
|
+
'franklin',
|
|
12168
|
+
'andre',
|
|
12169
|
+
'elmer',
|
|
12170
|
+
'brad',
|
|
12171
|
+
'gabriel',
|
|
12172
|
+
'ron',
|
|
12173
|
+
'mitchell',
|
|
12174
|
+
'roland',
|
|
12175
|
+
'arnold',
|
|
12176
|
+
'harvey',
|
|
12177
|
+
'jared',
|
|
12178
|
+
'adrian',
|
|
12179
|
+
'karl',
|
|
12180
|
+
'cory',
|
|
12181
|
+
'claude',
|
|
12182
|
+
'erik',
|
|
12183
|
+
'darryl',
|
|
12184
|
+
'jamie',
|
|
12185
|
+
'neil',
|
|
12186
|
+
'jessie',
|
|
12187
|
+
'christian',
|
|
12188
|
+
'javier',
|
|
12189
|
+
'fernando',
|
|
12190
|
+
'clinton',
|
|
12191
|
+
'ted',
|
|
12192
|
+
'mathew',
|
|
12193
|
+
'tyrone',
|
|
12194
|
+
'darren',
|
|
12195
|
+
'lonnie',
|
|
12196
|
+
'lance',
|
|
12197
|
+
'cody',
|
|
12198
|
+
'julio',
|
|
12199
|
+
'kelly',
|
|
12200
|
+
'kurt',
|
|
12201
|
+
'allan',
|
|
12202
|
+
'nelson',
|
|
12203
|
+
'guy',
|
|
12204
|
+
'clayton',
|
|
12205
|
+
'hugh',
|
|
12206
|
+
'max',
|
|
12207
|
+
'dwayne',
|
|
12208
|
+
'dwight',
|
|
12209
|
+
'armando',
|
|
12210
|
+
'felix',
|
|
12211
|
+
'jimmie',
|
|
12212
|
+
'everett',
|
|
12213
|
+
'jordan',
|
|
12214
|
+
'ian',
|
|
12215
|
+
'wallace',
|
|
12216
|
+
'ken',
|
|
12217
|
+
'bob',
|
|
12218
|
+
'jaime',
|
|
12219
|
+
'casey',
|
|
12220
|
+
'alfredo',
|
|
12221
|
+
'alberto',
|
|
12222
|
+
'dave',
|
|
12223
|
+
'ivan',
|
|
12224
|
+
'johnnie',
|
|
12225
|
+
'sidney',
|
|
12226
|
+
'byron',
|
|
12227
|
+
'julian',
|
|
12228
|
+
'isaac',
|
|
12229
|
+
'morris',
|
|
12230
|
+
'clifton',
|
|
12231
|
+
'willard',
|
|
12232
|
+
'daryl',
|
|
12233
|
+
'ross',
|
|
12234
|
+
'virgil',
|
|
12235
|
+
'andy',
|
|
12236
|
+
'marshall',
|
|
12237
|
+
'salvador',
|
|
12238
|
+
'perry',
|
|
12239
|
+
'kirk',
|
|
12240
|
+
'sergio',
|
|
12241
|
+
'marion',
|
|
12242
|
+
'tracy',
|
|
12243
|
+
'seth',
|
|
12244
|
+
'kent',
|
|
12245
|
+
'terrance',
|
|
12246
|
+
'rene',
|
|
12247
|
+
'eduardo',
|
|
12248
|
+
'terrence',
|
|
12249
|
+
'enrique',
|
|
12250
|
+
'freddie',
|
|
12251
|
+
'wade',
|
|
12252
|
+
'austin',
|
|
12253
|
+
'stuart',
|
|
12254
|
+
'fredrick',
|
|
12255
|
+
'arturo',
|
|
12256
|
+
'alejandro',
|
|
12257
|
+
'jackie',
|
|
12258
|
+
'joey',
|
|
12259
|
+
'nick',
|
|
12260
|
+
'luther',
|
|
12261
|
+
'wendell',
|
|
12262
|
+
'jeremiah',
|
|
12263
|
+
'evan',
|
|
12264
|
+
'julius',
|
|
12265
|
+
'dana',
|
|
12266
|
+
'donnie',
|
|
12267
|
+
'otis',
|
|
12268
|
+
'shannon',
|
|
12269
|
+
'trevor',
|
|
12270
|
+
'oliver',
|
|
12271
|
+
'luke',
|
|
12272
|
+
'homer',
|
|
12273
|
+
'gerard',
|
|
12274
|
+
'doug',
|
|
12275
|
+
'kenny',
|
|
12276
|
+
'hubert',
|
|
12277
|
+
'angelo',
|
|
12278
|
+
'shaun',
|
|
12279
|
+
'lyle',
|
|
12280
|
+
'matt',
|
|
12281
|
+
'lynn',
|
|
12282
|
+
'alfonso',
|
|
12283
|
+
'orlando',
|
|
12284
|
+
'rex',
|
|
12285
|
+
'carlton',
|
|
12286
|
+
'ernesto',
|
|
12287
|
+
'cameron',
|
|
12288
|
+
'neal',
|
|
12289
|
+
'pablo',
|
|
12290
|
+
'lorenzo',
|
|
12291
|
+
'omar',
|
|
12292
|
+
'wilbur',
|
|
12293
|
+
'blake',
|
|
12294
|
+
'grant',
|
|
12295
|
+
'horace',
|
|
12296
|
+
'roderick',
|
|
12297
|
+
'kerry',
|
|
12298
|
+
'abraham',
|
|
12299
|
+
'willis',
|
|
12300
|
+
'rickey',
|
|
12301
|
+
'jean',
|
|
12302
|
+
'ira',
|
|
12303
|
+
'andres',
|
|
12304
|
+
'cesar',
|
|
12305
|
+
'johnathan',
|
|
12306
|
+
'malcolm',
|
|
12307
|
+
'rudolph',
|
|
12308
|
+
'damon',
|
|
12309
|
+
'kelvin',
|
|
12310
|
+
'rudy',
|
|
12311
|
+
'preston',
|
|
12312
|
+
'alton',
|
|
12313
|
+
'archie',
|
|
12314
|
+
'marco',
|
|
12315
|
+
'wm',
|
|
12316
|
+
'pete',
|
|
12317
|
+
'randolph',
|
|
12318
|
+
'garry',
|
|
12319
|
+
'geoffrey',
|
|
12320
|
+
'jonathon',
|
|
12321
|
+
'felipe',
|
|
12322
|
+
'bennie',
|
|
12323
|
+
'gerardo',
|
|
12324
|
+
'ed',
|
|
12325
|
+
'dominique',
|
|
12326
|
+
'robin',
|
|
12327
|
+
'loren',
|
|
12328
|
+
'delbert',
|
|
12329
|
+
'colin',
|
|
12330
|
+
'guillermo',
|
|
12331
|
+
'earnest',
|
|
12332
|
+
'lucas',
|
|
12333
|
+
'benny',
|
|
12334
|
+
'noel',
|
|
12335
|
+
'spencer',
|
|
12336
|
+
'rodolfo',
|
|
12337
|
+
'myron',
|
|
12338
|
+
'edmund',
|
|
12339
|
+
'garrett',
|
|
12340
|
+
'salvatore',
|
|
12341
|
+
'cedric',
|
|
12342
|
+
'lowell',
|
|
12343
|
+
'gregg',
|
|
12344
|
+
'sherman',
|
|
12345
|
+
'wilson',
|
|
12346
|
+
'devin',
|
|
12347
|
+
'kristopher',
|
|
12348
|
+
'sylvester',
|
|
12349
|
+
'roosevelt',
|
|
12350
|
+
'israel',
|
|
12351
|
+
'jermaine',
|
|
12352
|
+
'forrest',
|
|
12353
|
+
'wilbert',
|
|
12354
|
+
'leland',
|
|
12355
|
+
'simon',
|
|
12356
|
+
'guadalupe',
|
|
12357
|
+
'clark',
|
|
12358
|
+
'irving',
|
|
12359
|
+
'carroll',
|
|
12360
|
+
'bryant',
|
|
12361
|
+
'owen',
|
|
12362
|
+
'rufus',
|
|
12363
|
+
'woodrow',
|
|
12364
|
+
'sammy',
|
|
12365
|
+
'kristopher',
|
|
12366
|
+
'mack',
|
|
12367
|
+
'levi',
|
|
12368
|
+
'marcos',
|
|
12369
|
+
'gustavo',
|
|
12370
|
+
'jake',
|
|
12371
|
+
'lionel',
|
|
12372
|
+
'marty',
|
|
12373
|
+
'taylor',
|
|
12374
|
+
'ellis',
|
|
12375
|
+
'dallas',
|
|
12376
|
+
'gilberto',
|
|
12377
|
+
'clint',
|
|
12378
|
+
'nicolas',
|
|
12379
|
+
'laurence',
|
|
12380
|
+
'ismael',
|
|
12381
|
+
'orville',
|
|
12382
|
+
'drew',
|
|
12383
|
+
'jody',
|
|
12384
|
+
'ervin',
|
|
12385
|
+
'dewey',
|
|
12386
|
+
'al',
|
|
12387
|
+
'wilfred',
|
|
12388
|
+
'josh',
|
|
12389
|
+
'hugo',
|
|
12390
|
+
'ignacio',
|
|
12391
|
+
'caleb',
|
|
12392
|
+
'tomas',
|
|
12393
|
+
'sheldon',
|
|
12394
|
+
'erick',
|
|
12395
|
+
'frankie',
|
|
12396
|
+
'stewart',
|
|
12397
|
+
'doyle',
|
|
12398
|
+
'darrel',
|
|
12399
|
+
'rogelio',
|
|
12400
|
+
'terence',
|
|
12401
|
+
'santiago',
|
|
12402
|
+
'alonzo',
|
|
12403
|
+
'elias',
|
|
12404
|
+
'bert',
|
|
12405
|
+
'elbert',
|
|
12406
|
+
'ramiro',
|
|
12407
|
+
'conrad',
|
|
12408
|
+
'pat',
|
|
12409
|
+
'noah',
|
|
12410
|
+
'grady',
|
|
12411
|
+
'phil',
|
|
12412
|
+
'cornelius',
|
|
12413
|
+
'lamar',
|
|
12414
|
+
'rolando',
|
|
12415
|
+
'clay',
|
|
12416
|
+
'percy',
|
|
12417
|
+
'dexter',
|
|
12418
|
+
'bradford',
|
|
12419
|
+
'merle',
|
|
12420
|
+
'darin',
|
|
12421
|
+
'amos',
|
|
12422
|
+
'terrell',
|
|
12423
|
+
'moses',
|
|
12424
|
+
'irvin',
|
|
12425
|
+
'saul',
|
|
12426
|
+
'roman',
|
|
12427
|
+
'darnell',
|
|
12428
|
+
'randal',
|
|
12429
|
+
'tommie',
|
|
12430
|
+
'timmy',
|
|
12431
|
+
'darrin',
|
|
12432
|
+
'winston',
|
|
12433
|
+
'brendan',
|
|
12434
|
+
'toby',
|
|
12435
|
+
'van',
|
|
12436
|
+
'abel',
|
|
12437
|
+
'dominick',
|
|
12438
|
+
'boyd',
|
|
12439
|
+
'courtney',
|
|
12440
|
+
'jan',
|
|
12441
|
+
'emilio',
|
|
12442
|
+
'elijah',
|
|
12443
|
+
'cary',
|
|
12444
|
+
'domenic',
|
|
12445
|
+
'emmanuel',
|
|
12446
|
+
'stephon',
|
|
12447
|
+
'lon',
|
|
12448
|
+
'brice',
|
|
12449
|
+
'judson',
|
|
12450
|
+
'antoine',
|
|
12451
|
+
'jerrod',
|
|
12452
|
+
'foster',
|
|
12453
|
+
'reynaldo',
|
|
12454
|
+
'reinaldo',
|
|
12455
|
+
'cristobal',
|
|
12456
|
+
'demetrius',
|
|
12457
|
+
'carlo',
|
|
12458
|
+
'nolan',
|
|
12459
|
+
'rod',
|
|
12460
|
+
'quinton',
|
|
12461
|
+
'hal',
|
|
12462
|
+
'brain',
|
|
12463
|
+
'rob',
|
|
12464
|
+
'elwood',
|
|
12465
|
+
'kendrick',
|
|
12466
|
+
'darius',
|
|
12467
|
+
'moises',
|
|
12468
|
+
'son',
|
|
12469
|
+
'marlin',
|
|
12470
|
+
'fidel',
|
|
12471
|
+
'thaddeus',
|
|
12472
|
+
'cliff',
|
|
12473
|
+
'marcel',
|
|
12474
|
+
'ali',
|
|
12475
|
+
'jackson',
|
|
12476
|
+
'raphael',
|
|
12477
|
+
'bryon',
|
|
12478
|
+
'armand',
|
|
12479
|
+
'alvaro',
|
|
12480
|
+
'jeffry',
|
|
12481
|
+
'dane',
|
|
12482
|
+
'joesph',
|
|
12483
|
+
'thurman',
|
|
12484
|
+
'ned',
|
|
12485
|
+
'sammie',
|
|
12486
|
+
'rusty',
|
|
12487
|
+
'michel',
|
|
12488
|
+
'monty',
|
|
12489
|
+
'rory',
|
|
12490
|
+
'fabian',
|
|
12491
|
+
'reggie',
|
|
12492
|
+
'mason',
|
|
12493
|
+
'graham',
|
|
12494
|
+
'kris',
|
|
12495
|
+
'isaiah',
|
|
12496
|
+
'vaughn',
|
|
12497
|
+
'gus',
|
|
12498
|
+
'avery',
|
|
12499
|
+
'loyd',
|
|
12500
|
+
'diego',
|
|
12501
|
+
'alexis',
|
|
12502
|
+
'adolph',
|
|
12503
|
+
'norris',
|
|
12504
|
+
'millard',
|
|
12505
|
+
'rocco',
|
|
12506
|
+
'gonzalo',
|
|
12507
|
+
'derick',
|
|
12508
|
+
'rodrigo',
|
|
12509
|
+
'gerry',
|
|
12510
|
+
'stacey',
|
|
12511
|
+
'trent',
|
|
12512
|
+
'carmen',
|
|
12513
|
+
'wiley',
|
|
12514
|
+
'rigoberto',
|
|
12515
|
+
'alphonso',
|
|
12516
|
+
'ty',
|
|
12517
|
+
'shelby',
|
|
12518
|
+
'rickie',
|
|
12519
|
+
'noe',
|
|
12520
|
+
'vern',
|
|
12521
|
+
'bobbie',
|
|
12522
|
+
'reed',
|
|
12523
|
+
'jefferson',
|
|
12524
|
+
'elvis',
|
|
12525
|
+
'bernardo',
|
|
12526
|
+
'mauricio',
|
|
12527
|
+
'hiram',
|
|
12528
|
+
'donovan',
|
|
12529
|
+
'basil',
|
|
12530
|
+
'riley',
|
|
12531
|
+
'ollie',
|
|
12532
|
+
'nickolas',
|
|
12533
|
+
'maynard',
|
|
12534
|
+
'scotty',
|
|
12535
|
+
'carson',
|
|
12536
|
+
'rae',
|
|
12537
|
+
'abdul',
|
|
12538
|
+
'langston',
|
|
12539
|
+
'dex',
|
|
12540
|
+
'hassan',
|
|
12541
|
+
'jamal',
|
|
12542
|
+
'dwain',
|
|
12543
|
+
'grover',
|
|
12544
|
+
'porter',
|
|
12545
|
+
'ervin',
|
|
12546
|
+
'lenny',
|
|
12547
|
+
'trenton',
|
|
12548
|
+
'denny',
|
|
12549
|
+
'fabian',
|
|
12550
|
+
'michal',
|
|
12551
|
+
'mikel',
|
|
12552
|
+
'dorian',
|
|
12553
|
+
'britt',
|
|
12554
|
+
'bennet',
|
|
12555
|
+
'sanford',
|
|
12556
|
+
'chaim',
|
|
12557
|
+
'otis',
|
|
12558
|
+
'reuben',
|
|
12559
|
+
'stewart',
|
|
12560
|
+
'maxwell',
|
|
12561
|
+
'dirk',
|
|
12562
|
+
'nehemiah',
|
|
12563
|
+
'levi',
|
|
12564
|
+
'romeo',
|
|
12565
|
+
'jarod',
|
|
12566
|
+
'vance',
|
|
12567
|
+
'damion',
|
|
12568
|
+
'mohammad',
|
|
12569
|
+
'zachery',
|
|
12570
|
+
'tad',
|
|
12571
|
+
'shon',
|
|
12572
|
+
'michale',
|
|
12573
|
+
'blair',
|
|
12574
|
+
'tory',
|
|
12575
|
+
'shad',
|
|
12576
|
+
'cole',
|
|
12577
|
+
'duncan',
|
|
12578
|
+
'bertram',
|
|
12579
|
+
'brant',
|
|
12580
|
+
'wilfredo',
|
|
12581
|
+
'hershel',
|
|
12582
|
+
'eliseo',
|
|
12583
|
+
'donny',
|
|
12584
|
+
'dillon',
|
|
12585
|
+
'robt',
|
|
12586
|
+
'wilford',
|
|
12587
|
+
'barton',
|
|
12588
|
+
'stacy',
|
|
12589
|
+
'elliott',
|
|
12590
|
+
'darron',
|
|
12591
|
+
'cortney',
|
|
12592
|
+
'chance',
|
|
12593
|
+
'denver',
|
|
12594
|
+
'elvin',
|
|
12595
|
+
'norbert',
|
|
12596
|
+
'giovanni',
|
|
12597
|
+
'stevie',
|
|
12598
|
+
'barney',
|
|
12599
|
+
'nestor',
|
|
12600
|
+
'hollis',
|
|
12601
|
+
'stefan',
|
|
12602
|
+
'donn',
|
|
12603
|
+
'linwood',
|
|
12604
|
+
'lauren',
|
|
12605
|
+
'tyree',
|
|
12606
|
+
'jeramy',
|
|
12607
|
+
'jed',
|
|
12608
|
+
'efren',
|
|
12609
|
+
'antony',
|
|
12610
|
+
'ahmad',
|
|
12611
|
+
'sung',
|
|
12612
|
+
'harrison',
|
|
12613
|
+
'antione',
|
|
12614
|
+
'waldo',
|
|
12615
|
+
'fredric',
|
|
12616
|
+
'bradly',
|
|
12617
|
+
'quincy',
|
|
12618
|
+
'matt',
|
|
12619
|
+
'hunter',
|
|
12620
|
+
];
|
|
12621
|
+
|
|
12622
|
+
// Common female first names
|
|
12623
|
+
const femaleNames = [
|
|
12624
|
+
'mary',
|
|
12625
|
+
'patricia',
|
|
12626
|
+
'linda',
|
|
12627
|
+
'barbara',
|
|
12628
|
+
'elizabeth',
|
|
12629
|
+
'jennifer',
|
|
12630
|
+
'maria',
|
|
12631
|
+
'susan',
|
|
12632
|
+
'margaret',
|
|
12633
|
+
'dorothy',
|
|
12634
|
+
'lisa',
|
|
12635
|
+
'nancy',
|
|
12636
|
+
'karen',
|
|
12637
|
+
'betty',
|
|
12638
|
+
'helen',
|
|
12639
|
+
'sandra',
|
|
12640
|
+
'donna',
|
|
12641
|
+
'carol',
|
|
12642
|
+
'ruth',
|
|
12643
|
+
'sharon',
|
|
12644
|
+
'michelle',
|
|
12645
|
+
'laura',
|
|
12646
|
+
'sarah',
|
|
12647
|
+
'kimberly',
|
|
12648
|
+
'deborah',
|
|
12649
|
+
'jessica',
|
|
12650
|
+
'shirley',
|
|
12651
|
+
'cynthia',
|
|
12652
|
+
'angela',
|
|
12653
|
+
'melissa',
|
|
12654
|
+
'brenda',
|
|
12655
|
+
'amy',
|
|
12656
|
+
'anna',
|
|
12657
|
+
'rebecca',
|
|
12658
|
+
'virginia',
|
|
12659
|
+
'kathleen',
|
|
12660
|
+
'pamela',
|
|
12661
|
+
'martha',
|
|
12662
|
+
'debra',
|
|
12663
|
+
'amanda',
|
|
12664
|
+
'stephanie',
|
|
12665
|
+
'carolyn',
|
|
12666
|
+
'christine',
|
|
12667
|
+
'marie',
|
|
12668
|
+
'janet',
|
|
12669
|
+
'catherine',
|
|
12670
|
+
'frances',
|
|
12671
|
+
'ann',
|
|
12672
|
+
'joyce',
|
|
12673
|
+
'diane',
|
|
12674
|
+
'alice',
|
|
12675
|
+
'julie',
|
|
12676
|
+
'heather',
|
|
12677
|
+
'teresa',
|
|
12678
|
+
'doris',
|
|
12679
|
+
'gloria',
|
|
12680
|
+
'evelyn',
|
|
12681
|
+
'jean',
|
|
12682
|
+
'cheryl',
|
|
12683
|
+
'mildred',
|
|
12684
|
+
'katherine',
|
|
12685
|
+
'joan',
|
|
12686
|
+
'ashley',
|
|
12687
|
+
'judith',
|
|
12688
|
+
'rose',
|
|
12689
|
+
'janice',
|
|
12690
|
+
'kelly',
|
|
12691
|
+
'nicole',
|
|
12692
|
+
'judy',
|
|
12693
|
+
'christina',
|
|
12694
|
+
'kathy',
|
|
12695
|
+
'theresa',
|
|
12696
|
+
'beverly',
|
|
12697
|
+
'denise',
|
|
12698
|
+
'tammy',
|
|
12699
|
+
'irene',
|
|
12700
|
+
'jane',
|
|
12701
|
+
'lori',
|
|
12702
|
+
'rachel',
|
|
12703
|
+
'marilyn',
|
|
12704
|
+
'andrea',
|
|
12705
|
+
'kathryn',
|
|
12706
|
+
'louise',
|
|
12707
|
+
'sara',
|
|
12708
|
+
'anne',
|
|
12709
|
+
'jacqueline',
|
|
12710
|
+
'wanda',
|
|
12711
|
+
'bonnie',
|
|
12712
|
+
'julia',
|
|
12713
|
+
'ruby',
|
|
12714
|
+
'lois',
|
|
12715
|
+
'tina',
|
|
12716
|
+
'phyllis',
|
|
12717
|
+
'norma',
|
|
12718
|
+
'paula',
|
|
12719
|
+
'diana',
|
|
12720
|
+
'annie',
|
|
12721
|
+
'lillian',
|
|
12722
|
+
'emily',
|
|
12723
|
+
'robin',
|
|
12724
|
+
'peggy',
|
|
12725
|
+
'crystal',
|
|
12726
|
+
'gladys',
|
|
12727
|
+
'rita',
|
|
12728
|
+
'dawn',
|
|
12729
|
+
'connie',
|
|
12730
|
+
'florence',
|
|
12731
|
+
'tracy',
|
|
12732
|
+
'edna',
|
|
12733
|
+
'tiffany',
|
|
12734
|
+
'carmen',
|
|
12735
|
+
'rosa',
|
|
12736
|
+
'cindy',
|
|
12737
|
+
'grace',
|
|
12738
|
+
'wendy',
|
|
12739
|
+
'victoria',
|
|
12740
|
+
'edith',
|
|
12741
|
+
'kim',
|
|
12742
|
+
'sherry',
|
|
12743
|
+
'sylvia',
|
|
12744
|
+
'josephine',
|
|
12745
|
+
'thelma',
|
|
12746
|
+
'shannon',
|
|
12747
|
+
'sheila',
|
|
12748
|
+
'ethel',
|
|
12749
|
+
'ellen',
|
|
12750
|
+
'elaine',
|
|
12751
|
+
'marjorie',
|
|
12752
|
+
'carrie',
|
|
12753
|
+
'charlotte',
|
|
12754
|
+
'monica',
|
|
12755
|
+
'esther',
|
|
12756
|
+
'pauline',
|
|
12757
|
+
'emma',
|
|
12758
|
+
'juanita',
|
|
12759
|
+
'anita',
|
|
12760
|
+
'rhonda',
|
|
12761
|
+
'hazel',
|
|
12762
|
+
'amber',
|
|
12763
|
+
'eva',
|
|
12764
|
+
'debbie',
|
|
12765
|
+
'april',
|
|
12766
|
+
'leslie',
|
|
12767
|
+
'clara',
|
|
12768
|
+
'lucille',
|
|
12769
|
+
'jamie',
|
|
12770
|
+
'joanne',
|
|
12771
|
+
'eleanor',
|
|
12772
|
+
'valerie',
|
|
12773
|
+
'danielle',
|
|
12774
|
+
'megan',
|
|
12775
|
+
'alicia',
|
|
12776
|
+
'suzanne',
|
|
12777
|
+
'michele',
|
|
12778
|
+
'gail',
|
|
12779
|
+
'bertha',
|
|
12780
|
+
'darlene',
|
|
12781
|
+
'veronica',
|
|
12782
|
+
'jill',
|
|
12783
|
+
'erin',
|
|
12784
|
+
'geraldine',
|
|
12785
|
+
'lauren',
|
|
12786
|
+
'cathy',
|
|
12787
|
+
'joann',
|
|
12788
|
+
'lorraine',
|
|
12789
|
+
'lynn',
|
|
12790
|
+
'sally',
|
|
12791
|
+
'regina',
|
|
12792
|
+
'erica',
|
|
12793
|
+
'beatrice',
|
|
12794
|
+
'dolores',
|
|
12795
|
+
'bernice',
|
|
12796
|
+
'audrey',
|
|
12797
|
+
'yvonne',
|
|
12798
|
+
'annette',
|
|
12799
|
+
'june',
|
|
12800
|
+
'samantha',
|
|
12801
|
+
'marion',
|
|
12802
|
+
'dana',
|
|
12803
|
+
'stacy',
|
|
12804
|
+
'ana',
|
|
12805
|
+
'renee',
|
|
12806
|
+
'ida',
|
|
12807
|
+
'vivian',
|
|
12808
|
+
'roberta',
|
|
12809
|
+
'holly',
|
|
12810
|
+
'brittany',
|
|
12811
|
+
'melanie',
|
|
12812
|
+
'loretta',
|
|
12813
|
+
'yolanda',
|
|
12814
|
+
'jeanette',
|
|
12815
|
+
'laurie',
|
|
12816
|
+
'katie',
|
|
12817
|
+
'kristen',
|
|
12818
|
+
'vanessa',
|
|
12819
|
+
'alma',
|
|
12820
|
+
'sue',
|
|
12821
|
+
'elsie',
|
|
12822
|
+
'beth',
|
|
12823
|
+
'jeanne',
|
|
12824
|
+
'vicki',
|
|
12825
|
+
'carla',
|
|
12826
|
+
'tara',
|
|
12827
|
+
'rosemary',
|
|
12828
|
+
'eileen',
|
|
12829
|
+
'terri',
|
|
12830
|
+
'gertrude',
|
|
12831
|
+
'lucy',
|
|
12832
|
+
'tonya',
|
|
12833
|
+
'ella',
|
|
12834
|
+
'stacey',
|
|
12835
|
+
'wilma',
|
|
12836
|
+
'gina',
|
|
12837
|
+
'kristin',
|
|
12838
|
+
'jessie',
|
|
12839
|
+
'natalie',
|
|
12840
|
+
'agnes',
|
|
12841
|
+
'vera',
|
|
12842
|
+
'willie',
|
|
12843
|
+
'charlene',
|
|
12844
|
+
'bessie',
|
|
12845
|
+
'delores',
|
|
12846
|
+
'melinda',
|
|
12847
|
+
'pearl',
|
|
12848
|
+
'arlene',
|
|
12849
|
+
'maureen',
|
|
12850
|
+
'colleen',
|
|
12851
|
+
'allison',
|
|
12852
|
+
'tamara',
|
|
12853
|
+
'joy',
|
|
12854
|
+
'georgia',
|
|
12855
|
+
'constance',
|
|
12856
|
+
'lillie',
|
|
12857
|
+
'claudia',
|
|
12858
|
+
'jackie',
|
|
12859
|
+
'marcia',
|
|
12860
|
+
'tanya',
|
|
12861
|
+
'nellie',
|
|
12862
|
+
'minnie',
|
|
12863
|
+
'marlene',
|
|
12864
|
+
'heidi',
|
|
12865
|
+
'glenda',
|
|
12866
|
+
'lydia',
|
|
12867
|
+
'viola',
|
|
12868
|
+
'courtney',
|
|
12869
|
+
'marian',
|
|
12870
|
+
'stella',
|
|
12871
|
+
'caroline',
|
|
12872
|
+
'dora',
|
|
12873
|
+
'jo',
|
|
12874
|
+
'vickie',
|
|
12875
|
+
'mattie',
|
|
12876
|
+
'terry',
|
|
12877
|
+
'maxine',
|
|
12878
|
+
'irma',
|
|
12879
|
+
'mabel',
|
|
12880
|
+
'marsha',
|
|
12881
|
+
'myrtle',
|
|
12882
|
+
'lena',
|
|
12883
|
+
'christy',
|
|
12884
|
+
'deanna',
|
|
12885
|
+
'patsy',
|
|
12886
|
+
'hilda',
|
|
12887
|
+
'gwendolyn',
|
|
12888
|
+
'jenny',
|
|
12889
|
+
'nora',
|
|
12890
|
+
'margie',
|
|
12891
|
+
'nina',
|
|
12892
|
+
'cassandra',
|
|
12893
|
+
'leah',
|
|
12894
|
+
'penny',
|
|
12895
|
+
'kay',
|
|
12896
|
+
'priscilla',
|
|
12897
|
+
'naomi',
|
|
12898
|
+
'carole',
|
|
12899
|
+
'brandy',
|
|
12900
|
+
'olga',
|
|
12901
|
+
'billie',
|
|
12902
|
+
'dianne',
|
|
12903
|
+
'tracey',
|
|
12904
|
+
'leona',
|
|
12905
|
+
'jenny',
|
|
12906
|
+
'felicia',
|
|
12907
|
+
'sonia',
|
|
12908
|
+
'miriam',
|
|
12909
|
+
'velma',
|
|
12910
|
+
'becky',
|
|
12911
|
+
'bobbie',
|
|
12912
|
+
'violet',
|
|
12913
|
+
'kristina',
|
|
12914
|
+
'toni',
|
|
12915
|
+
'misty',
|
|
12916
|
+
'mae',
|
|
12917
|
+
'shelly',
|
|
12918
|
+
'daisy',
|
|
12919
|
+
'ramona',
|
|
12920
|
+
'sherri',
|
|
12921
|
+
'erika',
|
|
12922
|
+
'katrina',
|
|
12923
|
+
'claire',
|
|
12924
|
+
'lindsey',
|
|
12925
|
+
'lindsay',
|
|
12926
|
+
'geneva',
|
|
12927
|
+
'guadalupe',
|
|
12928
|
+
'belinda',
|
|
12929
|
+
'margarita',
|
|
12930
|
+
'sheryl',
|
|
12931
|
+
'cora',
|
|
12932
|
+
'faye',
|
|
12933
|
+
'ada',
|
|
12934
|
+
'natasha',
|
|
12935
|
+
'sabrina',
|
|
12936
|
+
'isabel',
|
|
12937
|
+
'marguerite',
|
|
12938
|
+
'hattie',
|
|
12939
|
+
'harriet',
|
|
12940
|
+
'molly',
|
|
12941
|
+
'cecilia',
|
|
12942
|
+
'kristi',
|
|
12943
|
+
'brandi',
|
|
12944
|
+
'blanche',
|
|
12945
|
+
'sandy',
|
|
12946
|
+
'rosie',
|
|
12947
|
+
'joanna',
|
|
12948
|
+
'iris',
|
|
12949
|
+
'eunice',
|
|
12950
|
+
'angie',
|
|
12951
|
+
'inez',
|
|
12952
|
+
'lynda',
|
|
12953
|
+
'madeline',
|
|
12954
|
+
'amelia',
|
|
12955
|
+
'alberta',
|
|
12956
|
+
'genevieve',
|
|
12957
|
+
'monique',
|
|
12958
|
+
'jodi',
|
|
12959
|
+
'janie',
|
|
12960
|
+
'maggie',
|
|
12961
|
+
'kayla',
|
|
12962
|
+
'sonya',
|
|
12963
|
+
'jan',
|
|
12964
|
+
'lee',
|
|
12965
|
+
'kristine',
|
|
12966
|
+
'candace',
|
|
12967
|
+
'fannie',
|
|
12968
|
+
'maryann',
|
|
12969
|
+
'opal',
|
|
12970
|
+
'alison',
|
|
12971
|
+
'yvette',
|
|
12972
|
+
'melody',
|
|
12973
|
+
'luz',
|
|
12974
|
+
'susie',
|
|
12975
|
+
'olivia',
|
|
12976
|
+
'flora',
|
|
12977
|
+
'shelley',
|
|
12978
|
+
'kristy',
|
|
12979
|
+
'mamie',
|
|
12980
|
+
'lula',
|
|
12981
|
+
'lola',
|
|
12982
|
+
'verna',
|
|
12983
|
+
'beulah',
|
|
12984
|
+
'antoinette',
|
|
12985
|
+
'candice',
|
|
12986
|
+
'juana',
|
|
12987
|
+
'jeannette',
|
|
12988
|
+
'pam',
|
|
12989
|
+
'kelli',
|
|
12990
|
+
'hannah',
|
|
12991
|
+
'whitney',
|
|
12992
|
+
'bridget',
|
|
12993
|
+
'karla',
|
|
12994
|
+
'celia',
|
|
12995
|
+
'latoya',
|
|
12996
|
+
'patty',
|
|
12997
|
+
'shelia',
|
|
12998
|
+
'gayle',
|
|
12999
|
+
'della',
|
|
13000
|
+
'vicky',
|
|
13001
|
+
'lynne',
|
|
13002
|
+
'sheri',
|
|
13003
|
+
'marianne',
|
|
13004
|
+
'kara',
|
|
13005
|
+
'jacquelyn',
|
|
13006
|
+
'erma',
|
|
13007
|
+
'blanca',
|
|
13008
|
+
'myra',
|
|
13009
|
+
'leticia',
|
|
13010
|
+
'patrice',
|
|
13011
|
+
'rona',
|
|
13012
|
+
'kendall',
|
|
13013
|
+
'wendi',
|
|
13014
|
+
'sharron',
|
|
13015
|
+
'jocelyn',
|
|
13016
|
+
'angelica',
|
|
13017
|
+
'leah',
|
|
13018
|
+
'chrystal',
|
|
13019
|
+
'tia',
|
|
13020
|
+
'tricia',
|
|
13021
|
+
'francine',
|
|
13022
|
+
'leigh',
|
|
13023
|
+
'betsy',
|
|
13024
|
+
'elisa',
|
|
13025
|
+
'rachelle',
|
|
13026
|
+
'edwina',
|
|
13027
|
+
'tabitha',
|
|
13028
|
+
'janelle',
|
|
13029
|
+
'lora',
|
|
13030
|
+
'ciara',
|
|
13031
|
+
'marla',
|
|
13032
|
+
'tania',
|
|
13033
|
+
'marcy',
|
|
13034
|
+
'karin',
|
|
13035
|
+
'bridgette',
|
|
13036
|
+
'alexandria',
|
|
13037
|
+
'daniela',
|
|
13038
|
+
'kelsey',
|
|
13039
|
+
'tameka',
|
|
13040
|
+
'cathleen',
|
|
13041
|
+
'lizzie',
|
|
13042
|
+
'rochelle',
|
|
13043
|
+
'charla',
|
|
13044
|
+
'alisha',
|
|
13045
|
+
'lorena',
|
|
13046
|
+
'kaylee',
|
|
13047
|
+
'celeste',
|
|
13048
|
+
'brittney',
|
|
13049
|
+
'mercedes',
|
|
13050
|
+
'annabelle',
|
|
13051
|
+
'noelle',
|
|
13052
|
+
'sondra',
|
|
13053
|
+
'marisol',
|
|
13054
|
+
'karina',
|
|
13055
|
+
'ronda',
|
|
13056
|
+
'maritza',
|
|
13057
|
+
'tessa',
|
|
13058
|
+
'susana',
|
|
13059
|
+
'elena',
|
|
13060
|
+
'angelina',
|
|
13061
|
+
'carmela',
|
|
13062
|
+
'tiffani',
|
|
13063
|
+
'mackenzie',
|
|
13064
|
+
'kellie',
|
|
13065
|
+
'genevieve',
|
|
13066
|
+
'octavia',
|
|
13067
|
+
'janine',
|
|
13068
|
+
'carly',
|
|
13069
|
+
'kasey',
|
|
13070
|
+
'juliana',
|
|
13071
|
+
'gabrielle',
|
|
13072
|
+
'leanne',
|
|
13073
|
+
'katelyn',
|
|
13074
|
+
'dianna',
|
|
13075
|
+
'jaime',
|
|
13076
|
+
'roxanne',
|
|
13077
|
+
'simone',
|
|
13078
|
+
'julianne',
|
|
13079
|
+
'lacie',
|
|
13080
|
+
'latasha',
|
|
13081
|
+
'leann',
|
|
13082
|
+
'ashlee',
|
|
13083
|
+
'cara',
|
|
13084
|
+
'gretchen',
|
|
13085
|
+
'mallory',
|
|
13086
|
+
'shayla',
|
|
13087
|
+
'lara',
|
|
13088
|
+
'lindsey',
|
|
13089
|
+
'kerri',
|
|
13090
|
+
'desiree',
|
|
13091
|
+
'cassidy',
|
|
13092
|
+
'kaitlyn',
|
|
13093
|
+
'deana',
|
|
13094
|
+
'kira',
|
|
13095
|
+
'cristina',
|
|
13096
|
+
'sasha',
|
|
13097
|
+
'eliza',
|
|
13098
|
+
'melisa',
|
|
13099
|
+
'aubrey',
|
|
13100
|
+
'terrie',
|
|
13101
|
+
'teri',
|
|
13102
|
+
'lorene',
|
|
13103
|
+
'katharine',
|
|
13104
|
+
'jillian',
|
|
13105
|
+
'doreen',
|
|
13106
|
+
'hilary',
|
|
13107
|
+
'kathrine',
|
|
13108
|
+
'fay',
|
|
13109
|
+
'adrienne',
|
|
13110
|
+
'gwen',
|
|
13111
|
+
'marisa',
|
|
13112
|
+
'tasha',
|
|
13113
|
+
'silvia',
|
|
13114
|
+
'eula',
|
|
13115
|
+
'maribel',
|
|
13116
|
+
'susanna',
|
|
13117
|
+
'leila',
|
|
13118
|
+
'corinne',
|
|
13119
|
+
'jami',
|
|
13120
|
+
'alyce',
|
|
13121
|
+
'dina',
|
|
13122
|
+
'marcia',
|
|
13123
|
+
'leigh',
|
|
13124
|
+
'jenifer',
|
|
13125
|
+
'corrine',
|
|
13126
|
+
'hillary',
|
|
13127
|
+
'marci',
|
|
13128
|
+
'dixie',
|
|
13129
|
+
'deidre',
|
|
13130
|
+
'elise',
|
|
13131
|
+
'ines',
|
|
13132
|
+
'lily',
|
|
13133
|
+
'lenora',
|
|
13134
|
+
'marina',
|
|
13135
|
+
'nikki',
|
|
13136
|
+
'elsa',
|
|
13137
|
+
'roxane',
|
|
13138
|
+
'margo',
|
|
13139
|
+
'rene',
|
|
13140
|
+
'alyssa',
|
|
13141
|
+
'sherrie',
|
|
13142
|
+
'mollie',
|
|
13143
|
+
'alycia',
|
|
13144
|
+
'aurora',
|
|
13145
|
+
'lorelei',
|
|
13146
|
+
'joelle',
|
|
13147
|
+
'kayleigh',
|
|
13148
|
+
'jeanine',
|
|
13149
|
+
'shana',
|
|
13150
|
+
'sheree',
|
|
13151
|
+
'elaina',
|
|
13152
|
+
'elvira',
|
|
13153
|
+
'chandra',
|
|
13154
|
+
'marta',
|
|
13155
|
+
'marianna',
|
|
13156
|
+
'caitlin',
|
|
13157
|
+
'leanne',
|
|
13158
|
+
'jayne',
|
|
13159
|
+
'rosalind',
|
|
13160
|
+
'roxann',
|
|
13161
|
+
'dee',
|
|
13162
|
+
'marjory',
|
|
13163
|
+
'rosalie',
|
|
13164
|
+
'deann',
|
|
13165
|
+
'shanna',
|
|
13166
|
+
'angelia',
|
|
13167
|
+
'darlene',
|
|
13168
|
+
'juliet',
|
|
13169
|
+
'misti',
|
|
13170
|
+
'corina',
|
|
13171
|
+
'larissa',
|
|
13172
|
+
'noemi',
|
|
13173
|
+
'marlena',
|
|
13174
|
+
'tricia',
|
|
13175
|
+
'shelby',
|
|
13176
|
+
'carly',
|
|
13177
|
+
'julieann',
|
|
13178
|
+
'antonia',
|
|
13179
|
+
'candi',
|
|
13180
|
+
'luciana',
|
|
13181
|
+
'peggy',
|
|
13182
|
+
'georgina',
|
|
13183
|
+
'jasmine',
|
|
13184
|
+
'liza',
|
|
13185
|
+
'marybeth',
|
|
13186
|
+
'katarina',
|
|
13187
|
+
'marisela',
|
|
13188
|
+
'elva',
|
|
13189
|
+
'shanda',
|
|
13190
|
+
'terra',
|
|
13191
|
+
'juliann',
|
|
13192
|
+
'patrica',
|
|
13193
|
+
'bonita',
|
|
13194
|
+
'rosalinda',
|
|
13195
|
+
'maricela',
|
|
13196
|
+
'marla',
|
|
13197
|
+
'krystle',
|
|
13198
|
+
'kaylyn',
|
|
13199
|
+
'karissa',
|
|
13200
|
+
'shayna',
|
|
13201
|
+
'maris',
|
|
13202
|
+
'marcella',
|
|
13203
|
+
'deirdre',
|
|
13204
|
+
'darci',
|
|
13205
|
+
'arlene',
|
|
13206
|
+
'tabatha',
|
|
13207
|
+
'annmarie',
|
|
13208
|
+
'shauna',
|
|
13209
|
+
'rebekah',
|
|
13210
|
+
'deidra',
|
|
13211
|
+
'shawna',
|
|
13212
|
+
'paige',
|
|
13213
|
+
'aimee',
|
|
13214
|
+
'gabriela',
|
|
13215
|
+
'jodi',
|
|
13216
|
+
'kelli',
|
|
13217
|
+
'bridgett',
|
|
13218
|
+
'sadie',
|
|
13219
|
+
'june',
|
|
13220
|
+
'katy',
|
|
13221
|
+
'krystal',
|
|
13222
|
+
'tracie',
|
|
13223
|
+
'angelique',
|
|
13224
|
+
];
|
|
13225
|
+
|
|
13226
|
+
// Check for male names
|
|
13227
|
+
if (maleNames.includes(firstNameLower)) {
|
|
13228
|
+
return 'male';
|
|
13229
|
+
}
|
|
13230
|
+
|
|
13231
|
+
// Check for female names
|
|
13232
|
+
if (femaleNames.includes(firstNameLower)) {
|
|
13233
|
+
return 'female';
|
|
13234
|
+
}
|
|
13235
|
+
|
|
13236
|
+
// Check for unisex names that are commonly one gender
|
|
13237
|
+
const unisexMale = ['jordan', 'taylor', 'alex', 'jamie', 'casey', 'avery', 'riley', 'jesse', 'zane'];
|
|
13238
|
+
const unisexFemale = ['morgan', 'skyler', 'quinn', 'harper', 'payton', 'blake', 'reagan', 'sawyer', 'kendall'];
|
|
13239
|
+
|
|
13240
|
+
if (unisexMale.includes(firstNameLower)) {
|
|
13241
|
+
return 'male';
|
|
13242
|
+
}
|
|
13243
|
+
|
|
13244
|
+
if (unisexFemale.includes(firstNameLower)) {
|
|
13245
|
+
return 'female';
|
|
13246
|
+
}
|
|
13247
|
+
|
|
13248
|
+
return 'unknown';
|
|
13249
|
+
}
|
|
13250
|
+
|
|
13251
|
+
function inferNationalityFromName(firstName, lastName) {
|
|
13252
|
+
if (!firstName && !lastName) return 'unknown';
|
|
13253
|
+
|
|
13254
|
+
const fullName = `${firstName} ${lastName}`.toLowerCase();
|
|
13255
|
+
|
|
13256
|
+
// Common nationality patterns in names
|
|
13257
|
+
const nationalityPatterns = {
|
|
13258
|
+
// Asian names
|
|
13259
|
+
CN: ['li', 'wang', 'zhang', 'liu', 'chen', 'yang', 'zhao', 'huang', 'zhou', 'wu', 'xu', 'sun', 'ma', 'zhu', 'guo', 'lin', 'xie', 'he', 'gao'],
|
|
13260
|
+
JP: ['sato', 'suzuki', 'takahashi', 'tanaka', 'watanabe', 'ito', 'yamamoto', 'nakamura', 'kobayashi', 'kato', 'yoshida', 'yamada', 'sasaki', 'yamaguchi', 'matsumoto'],
|
|
13261
|
+
KR: ['kim', 'lee', 'park', 'choi', 'jung', 'kang', 'cho', 'yun', 'lim', 'jang', 'han', 'shin', 'seo', 'kwon', 'oh'],
|
|
13262
|
+
IN: ['patel', 'sharma', 'singh', 'kumar', 'gupta', 'reddy', 'mehta', 'rai', 'verma', 'jha', 'malik', 'yadav', 'thakur'],
|
|
13263
|
+
|
|
13264
|
+
// European names
|
|
13265
|
+
ES: ['garcia', 'rodriguez', 'gonzalez', 'fernandez', 'lopez', 'martinez', 'sanchez', 'perez', 'gomez', 'martin', 'jimenez', 'ruiz', 'hernandez', 'diaz', 'moreno'],
|
|
13266
|
+
FR: ['martin', 'bernard', 'dubois', 'thomas', 'robert', 'richard', 'petit', 'durand', 'leroy', 'moreau', 'simon', 'laurent', 'lefebvre', 'michel', 'garcia'],
|
|
13267
|
+
IT: ['rossi', 'russo', 'ferrari', 'esposito', 'bianchi', 'romano', 'colombo', 'ricci', 'marino', 'greco', 'bruno', 'gallo', 'conti', 'de luca', 'mancini'],
|
|
13268
|
+
DE: ['muller', 'schmidt', 'schneider', 'fischer', 'weber', 'meyer', 'wagner', 'becker', 'schulz', 'hoffmann', 'schäfer', 'koch', 'bauer', 'richter', 'klein'],
|
|
13269
|
+
GB: ['smith', 'jones', 'taylor', 'brown', 'williams', 'wilson', 'johnson', 'davis', 'robinson', 'wright', 'thompson', 'evans', 'walker', 'white', 'roberts'],
|
|
13270
|
+
|
|
13271
|
+
// Other regions
|
|
13272
|
+
BR: ['silva', 'santos', 'oliveira', 'souza', 'rodrigues', 'ferreira', 'alves', 'pereira', 'lima', 'gomes', 'costa', 'ribeiro', 'martins', 'carvalho', 'almeida'],
|
|
13273
|
+
RU: ['ivanov', 'smirnov', 'kuznetsov', 'popov', 'sokolov', 'lebedeva', 'kozlov', 'novikov', 'morozov', 'petrov', 'volkov', 'solovyov', 'vasilyev', 'zaitsev'],
|
|
13274
|
+
PL: ['nowak', 'kowalski', 'wisniewski', 'wojcik', 'kowalczyk', 'kaminski', 'lewandowski', 'zielinski', 'szymanski', 'wozniak', 'dabrowski', 'kozłowski', 'jankowski'],
|
|
13275
|
+
TR: ['yilmaz', 'kaya', 'demir', 'celik', 'sahin', 'yildiz', 'yildirim', 'ozturk', 'aydin', 'arslan', 'doğan', 'kural', 'aslan', 'çetin', 'kara'],
|
|
13276
|
+
};
|
|
13277
|
+
|
|
13278
|
+
// Check for patterns in last name
|
|
13279
|
+
for (const [countryCode, patterns] of Object.entries(nationalityPatterns)) {
|
|
13280
|
+
for (const pattern of patterns) {
|
|
13281
|
+
if (lastName.toLowerCase().includes(pattern.toLowerCase())) {
|
|
13282
|
+
return countryCode;
|
|
13283
|
+
}
|
|
13284
|
+
}
|
|
13285
|
+
}
|
|
13286
|
+
|
|
13287
|
+
// Check common first names by country
|
|
13288
|
+
const firstNamePatterns = {
|
|
13289
|
+
CN: ['wei', 'ming', 'li', 'jing', 'feng', 'yan', 'hua', 'xin', 'jun', 'lei'],
|
|
13290
|
+
JP: ['yuki', 'haru', 'aki', 'natsu', 'sora', 'hina', 'sakura', 'ren', 'yui', 'aoi'],
|
|
13291
|
+
KR: ['min', 'ji', 'seo', 'hyun', 'jung', 'soo', 'young', 'won', 'hae', 'jin'],
|
|
13292
|
+
IN: ['raj', 'dev', 'kumar', 'arun', 'vijay', 'suresh', 'anil', 'ram', 'hari', 'mohan'],
|
|
13293
|
+
ES: ['jose', 'juan', 'antonio', 'francisco', 'manuel', 'david', 'javier', 'daniel', 'carlos', 'alejandro'],
|
|
13294
|
+
FR: ['jean', 'pierre', 'michel', 'philippe', 'alain', 'nicolas', 'christophe', 'patrick', 'daniel', 'bernard'],
|
|
13295
|
+
IT: ['marco', 'andrea', 'alessandro', 'matteo', 'luca', 'giovanni', 'antonio', 'francesco', 'mario', 'luigi'],
|
|
13296
|
+
DE: ['hans', 'peter', 'wolfgang', 'michael', 'werner', 'klaus', 'thomas', 'helmut', 'gerhard', 'andreas'],
|
|
13297
|
+
RU: ['alexander', 'sergei', 'vladimir', 'dmitri', 'igor', 'nikolai', 'mikhail', 'yuri', 'oleg', 'pavel'],
|
|
13298
|
+
PL: ['jan', 'andrzej', 'piotr', 'krzysztof', 'stanislaw', 'tomasz', 'paweł', 'marek', 'michal', 'jerzy'],
|
|
13299
|
+
};
|
|
13300
|
+
|
|
13301
|
+
for (const [countryCode, patterns] of Object.entries(firstNamePatterns)) {
|
|
13302
|
+
for (const pattern of patterns) {
|
|
13303
|
+
if (firstName.toLowerCase().includes(pattern.toLowerCase())) {
|
|
13304
|
+
return countryCode;
|
|
13305
|
+
}
|
|
13306
|
+
}
|
|
13307
|
+
}
|
|
13308
|
+
|
|
13309
|
+
// Default to US for common English names
|
|
13310
|
+
const englishFirstNames = ['john', 'james', 'robert', 'michael', 'william', 'david', 'mary', 'patricia', 'linda', 'barbara'];
|
|
13311
|
+
if (englishFirstNames.includes(firstName.toLowerCase())) {
|
|
13312
|
+
return 'US';
|
|
13313
|
+
}
|
|
13314
|
+
|
|
13315
|
+
return 'unknown';
|
|
13316
|
+
}
|
|
13317
|
+
|
|
13318
|
+
async function hashString(str) {
|
|
13319
|
+
// Simple hash for context tracking
|
|
13320
|
+
const encoder = new TextEncoder();
|
|
13321
|
+
const data = encoder.encode(str);
|
|
13322
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
13323
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
13324
|
+
return hashArray
|
|
13325
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
13326
|
+
.join('')
|
|
13327
|
+
.substring(0, 16);
|
|
13328
|
+
}
|
|
13329
|
+
|
|
13330
|
+
// ========== LIGHT MODE FUNCTION ==========
|
|
13331
|
+
const getLightPersonInfo = (name, email) => {
|
|
13332
|
+
const nameParts = extractNameParts(name);
|
|
13333
|
+
const gender = inferGenderFromName(nameParts.firstName);
|
|
13334
|
+
const nationality = inferNationalityFromName(nameParts.firstName, nameParts.lastName);
|
|
13335
|
+
|
|
13336
|
+
return {
|
|
13337
|
+
person_full_name: name,
|
|
13338
|
+
person_first_name: nameParts.firstName,
|
|
13339
|
+
person_last_name: nameParts.lastName,
|
|
13340
|
+
person_gender: gender,
|
|
13341
|
+
person_nationality: nationality,
|
|
13342
|
+
source: 'light_mode_inference',
|
|
13343
|
+
};
|
|
13344
|
+
};
|
|
13345
|
+
|
|
13346
|
+
// ========== MAIN FUNCTION LOGIC ==========
|
|
13347
|
+
|
|
13348
|
+
// Check if light mode is requested
|
|
13349
|
+
if (options.light === true) {
|
|
13350
|
+
console.log(`[Person Info] Running in light mode for: ${name} (${email})`);
|
|
13351
|
+
return getLightPersonInfo(name, email);
|
|
13352
|
+
}
|
|
13353
|
+
|
|
13354
|
+
// Original full mode with web search continues below...
|
|
13355
|
+
function processPersonInfo(data, originalName, originalEmail, emailContext) {
|
|
13356
|
+
// Ensure basic fields are populated
|
|
13357
|
+
if (!data.person_first_name || !data.person_last_name) {
|
|
13358
|
+
const nameParts = originalName.trim().split(/\s+/);
|
|
13359
|
+
data.person_first_name = data.person_first_name || nameParts[0] || '';
|
|
13360
|
+
data.person_last_name = data.person_last_name || nameParts.slice(1).join(' ') || '';
|
|
13361
|
+
}
|
|
13362
|
+
|
|
13363
|
+
// Set full name if not provided
|
|
13364
|
+
if (!data.person_full_name) {
|
|
13365
|
+
data.person_full_name = `${data.person_first_name} ${data.person_last_name}`.trim();
|
|
13366
|
+
}
|
|
13367
|
+
|
|
13368
|
+
// Ensure email is set
|
|
13369
|
+
data.person_email = data.person_email || originalEmail;
|
|
13370
|
+
|
|
13371
|
+
// Process demographic information
|
|
13372
|
+
data.person_age = processAge(data.person_age);
|
|
13373
|
+
data.person_gender = data.person_gender || 'unknown';
|
|
13374
|
+
data.person_nationality = processNationality(data.person_nationality);
|
|
13375
|
+
|
|
13376
|
+
// Standardize location fields
|
|
13377
|
+
data.person_location_city = cleanText(data.person_location_city);
|
|
13378
|
+
data.person_location_state = cleanStateProvince(data.person_location_state);
|
|
13379
|
+
data.person_location_country = processCountryCode(data.person_location_country);
|
|
13380
|
+
|
|
13381
|
+
// Format full location
|
|
13382
|
+
if (!data.person_location_full || data.person_location_full === 'Not available') {
|
|
13383
|
+
const locationParts = [];
|
|
13384
|
+
if (data.person_location_city && data.person_location_city !== 'Not available') {
|
|
13385
|
+
locationParts.push(data.person_location_city);
|
|
13386
|
+
}
|
|
13387
|
+
if (data.person_location_state && data.person_location_state !== 'Not available') {
|
|
13388
|
+
locationParts.push(data.person_location_state);
|
|
13389
|
+
}
|
|
13390
|
+
if (data.person_location_country && data.person_location_country !== 'Not available') {
|
|
13391
|
+
locationParts.push(countryCodeToName(data.person_location_country));
|
|
13392
|
+
}
|
|
13393
|
+
data.person_location_full = locationParts.length > 0 ? locationParts.join(', ') : 'Not available';
|
|
13394
|
+
}
|
|
13395
|
+
|
|
13396
|
+
// Clean and validate URLs
|
|
13397
|
+
data.person_linkedin = validateURL(data.person_linkedin);
|
|
13398
|
+
data.person_twitter = validateURL(data.person_twitter);
|
|
13399
|
+
data.person_github = validateURL(data.person_github);
|
|
13400
|
+
data.person_website = validateURL(data.person_website);
|
|
13401
|
+
|
|
13402
|
+
// Clean phone number
|
|
13403
|
+
data.person_phone = validatePhoneNumber(data.person_phone);
|
|
13404
|
+
|
|
13405
|
+
// Ensure arrays are properly formatted
|
|
13406
|
+
data.person_skills = ensureArray(data.person_skills);
|
|
13407
|
+
data.person_expertise = ensureArray(data.person_expertise);
|
|
13408
|
+
data.person_education = ensureArray(data.person_education);
|
|
13409
|
+
data.person_languages = ensureArray(data.person_languages);
|
|
13410
|
+
data.person_interests = ensureArray(data.person_interests);
|
|
13411
|
+
data.person_context_insights = ensureArray(data.person_context_insights);
|
|
13412
|
+
|
|
13413
|
+
// Calculate additional metadata
|
|
13414
|
+
data.person_name_initials = getInitials(data.person_first_name, data.person_last_name);
|
|
13415
|
+
data.person_age_range = calculateAgeRange(data.person_age);
|
|
13416
|
+
data.person_seniority_score = calculateSeniorityScore(data);
|
|
13417
|
+
data.person_pronouns = getPronounsFromGender(data.person_gender);
|
|
13418
|
+
|
|
13419
|
+
// Extract insights from email context
|
|
13420
|
+
if (emailContext && !data.person_context_insights) {
|
|
13421
|
+
data.person_context_insights = extractContextInsights(emailContext);
|
|
13422
|
+
}
|
|
13423
|
+
|
|
13424
|
+
// Create structured objects for easier access
|
|
13425
|
+
data.person_demographics = {
|
|
13426
|
+
age: data.person_age,
|
|
13427
|
+
age_range: data.person_age_range,
|
|
13428
|
+
gender: data.person_gender,
|
|
13429
|
+
pronouns: data.person_pronouns,
|
|
13430
|
+
nationality: data.person_nationality,
|
|
13431
|
+
nationality_name: countryCodeToName(data.person_nationality),
|
|
13432
|
+
};
|
|
13433
|
+
|
|
13434
|
+
data.person_contact = {
|
|
13435
|
+
email: data.person_email,
|
|
13436
|
+
phone: data.person_phone,
|
|
13437
|
+
location: {
|
|
13438
|
+
city: data.person_location_city,
|
|
13439
|
+
state: data.person_location_state,
|
|
13440
|
+
country: data.person_location_country,
|
|
13441
|
+
country_name: countryCodeToName(data.person_location_country),
|
|
13442
|
+
full: data.person_location_full,
|
|
13443
|
+
},
|
|
13444
|
+
};
|
|
13445
|
+
|
|
13446
|
+
data.person_professional = {
|
|
13447
|
+
title: data.person_title,
|
|
13448
|
+
company: data.person_company,
|
|
13449
|
+
industry: data.person_industry,
|
|
13450
|
+
career_level: data.person_career_level,
|
|
13451
|
+
years_experience: data.person_years_experience,
|
|
13452
|
+
skills: data.person_skills,
|
|
13453
|
+
expertise: data.person_expertise,
|
|
13454
|
+
education: data.person_education,
|
|
13455
|
+
};
|
|
13456
|
+
|
|
13457
|
+
data.person_online = {
|
|
13458
|
+
linkedin: data.person_linkedin,
|
|
13459
|
+
twitter: data.person_twitter,
|
|
13460
|
+
github: data.person_github,
|
|
13461
|
+
website: data.person_website,
|
|
13462
|
+
other: data.person_social_other || [],
|
|
13463
|
+
};
|
|
13464
|
+
|
|
13465
|
+
// Add context metadata
|
|
13466
|
+
data.context_analysis = {
|
|
13467
|
+
has_context: !!emailContext,
|
|
13468
|
+
context_length: emailContext?.length || 0,
|
|
13469
|
+
extracted_insights: data.person_context_insights || [],
|
|
13470
|
+
context_useful: data.person_has_context_hints || false,
|
|
13471
|
+
};
|
|
13472
|
+
|
|
13473
|
+
// Add timestamp
|
|
13474
|
+
data.retrieved_at = new Date().toISOString();
|
|
13475
|
+
|
|
13476
|
+
return data;
|
|
13477
|
+
}
|
|
13478
|
+
|
|
13479
|
+
function processAge(ageInput) {
|
|
13480
|
+
if (!ageInput || ageInput === 'unknown' || ageInput === 'Not available') {
|
|
13481
|
+
return 'unknown';
|
|
13482
|
+
}
|
|
13483
|
+
|
|
13484
|
+
if (typeof ageInput === 'number') {
|
|
13485
|
+
return Math.max(18, Math.min(100, ageInput));
|
|
13486
|
+
}
|
|
13487
|
+
|
|
13488
|
+
if (typeof ageInput === 'string') {
|
|
13489
|
+
// Parse age ranges like "30-40", "35+", "mid-30s"
|
|
13490
|
+
const rangeMatch = ageInput.match(/(\d+)-(\d+)/);
|
|
13491
|
+
if (rangeMatch) {
|
|
13492
|
+
const avg = Math.floor((parseInt(rangeMatch[1]) + parseInt(rangeMatch[2])) / 2);
|
|
13493
|
+
return avg;
|
|
13494
|
+
}
|
|
13495
|
+
|
|
13496
|
+
const numberMatch = ageInput.match(/\d+/);
|
|
13497
|
+
if (numberMatch) {
|
|
13498
|
+
const age = parseInt(numberMatch[0]);
|
|
13499
|
+
if (age >= 18 && age <= 100) return age;
|
|
13500
|
+
}
|
|
13501
|
+
|
|
13502
|
+
// Handle textual age descriptions
|
|
13503
|
+
const text = ageInput.toLowerCase();
|
|
13504
|
+
if (text.includes('twent') || text.includes('20')) return 25;
|
|
13505
|
+
if (text.includes('thirt') || text.includes('30')) return 35;
|
|
13506
|
+
if (text.includes('fort') || text.includes('40')) return 45;
|
|
13507
|
+
if (text.includes('fift') || text.includes('50')) return 55;
|
|
13508
|
+
if (text.includes('sixt') || text.includes('60')) return 65;
|
|
13509
|
+
}
|
|
13510
|
+
|
|
13511
|
+
return 'unknown';
|
|
13512
|
+
}
|
|
13513
|
+
|
|
13514
|
+
function calculateAgeRange(age) {
|
|
13515
|
+
if (age === 'unknown' || typeof age !== 'number') return 'Unknown';
|
|
13516
|
+
|
|
13517
|
+
if (age < 25) return '18-24';
|
|
13518
|
+
if (age < 30) return '25-29';
|
|
13519
|
+
if (age < 35) return '30-34';
|
|
13520
|
+
if (age < 40) return '35-39';
|
|
13521
|
+
if (age < 50) return '40-49';
|
|
13522
|
+
if (age < 60) return '50-59';
|
|
13523
|
+
return '60+';
|
|
13524
|
+
}
|
|
13525
|
+
|
|
13526
|
+
function processNationality(nationality) {
|
|
13527
|
+
if (!nationality || nationality.toLowerCase() === 'unknown' || nationality.toLowerCase() === 'not available') {
|
|
13528
|
+
return 'unknown';
|
|
13529
|
+
}
|
|
13530
|
+
|
|
13531
|
+
// Convert to uppercase for country codes
|
|
13532
|
+
const upper = nationality.toUpperCase();
|
|
13533
|
+
|
|
13534
|
+
// Map common variations to ISO codes
|
|
13535
|
+
const countryMap = {
|
|
13536
|
+
USA: 'US',
|
|
13537
|
+
'UNITED STATES': 'US',
|
|
13538
|
+
AMERICA: 'US',
|
|
13539
|
+
UK: 'GB',
|
|
13540
|
+
'UNITED KINGDOM': 'GB',
|
|
13541
|
+
ENGLAND: 'GB',
|
|
13542
|
+
SCOTLAND: 'GB',
|
|
13543
|
+
CANADA: 'CA',
|
|
13544
|
+
AUSTRALIA: 'AU',
|
|
13545
|
+
INDIA: 'IN',
|
|
13546
|
+
GERMANY: 'DE',
|
|
13547
|
+
FRANCE: 'FR',
|
|
13548
|
+
CHINA: 'CN',
|
|
13549
|
+
JAPAN: 'JP',
|
|
13550
|
+
BRAZIL: 'BR',
|
|
13551
|
+
MEXICO: 'MX',
|
|
13552
|
+
SPAIN: 'ES',
|
|
13553
|
+
ITALY: 'IT',
|
|
13554
|
+
NETHERLANDS: 'NL',
|
|
13555
|
+
SWEDEN: 'SE',
|
|
13556
|
+
SWITZERLAND: 'CH',
|
|
13557
|
+
'SOUTH KOREA': 'KR',
|
|
13558
|
+
RUSSIA: 'RU',
|
|
13559
|
+
};
|
|
13560
|
+
|
|
13561
|
+
if (countryMap[upper]) {
|
|
13562
|
+
return countryMap[upper];
|
|
13563
|
+
}
|
|
13564
|
+
|
|
13565
|
+
// If it's already a 2-letter code, return it
|
|
13566
|
+
if (/^[A-Z]{2}$/.test(upper)) {
|
|
13567
|
+
return upper;
|
|
13568
|
+
}
|
|
13569
|
+
|
|
13570
|
+
return 'unknown';
|
|
13571
|
+
}
|
|
13572
|
+
|
|
13573
|
+
function processCountryCode(country) {
|
|
13574
|
+
if (!country || country.toLowerCase() === 'not available') {
|
|
13575
|
+
return 'unknown';
|
|
13576
|
+
}
|
|
13577
|
+
|
|
13578
|
+
return processNationality(country);
|
|
13579
|
+
}
|
|
13580
|
+
|
|
13581
|
+
function countryCodeToName(code) {
|
|
13582
|
+
if (!code || code === 'unknown') return 'Unknown';
|
|
13583
|
+
|
|
13584
|
+
const countryNames = {
|
|
13585
|
+
US: 'United States',
|
|
13586
|
+
GB: 'United Kingdom',
|
|
13587
|
+
CA: 'Canada',
|
|
13588
|
+
AU: 'Australia',
|
|
13589
|
+
DE: 'Germany',
|
|
13590
|
+
FR: 'France',
|
|
13591
|
+
JP: 'Japan',
|
|
13592
|
+
CN: 'China',
|
|
13593
|
+
IN: 'India',
|
|
13594
|
+
BR: 'Brazil',
|
|
13595
|
+
MX: 'Mexico',
|
|
13596
|
+
ES: 'Spain',
|
|
13597
|
+
IT: 'Italy',
|
|
13598
|
+
NL: 'Netherlands',
|
|
13599
|
+
SE: 'Sweden',
|
|
13600
|
+
CH: 'Switzerland',
|
|
13601
|
+
KR: 'South Korea',
|
|
13602
|
+
RU: 'Russia',
|
|
13603
|
+
};
|
|
13604
|
+
|
|
13605
|
+
return countryNames[code] || code;
|
|
13606
|
+
}
|
|
13607
|
+
|
|
13608
|
+
function getPronounsFromGender(gender) {
|
|
13609
|
+
switch (gender) {
|
|
13610
|
+
case 'male':
|
|
13611
|
+
return ['he/him', 'his'];
|
|
13612
|
+
case 'female':
|
|
13613
|
+
return ['she/her', 'hers'];
|
|
13614
|
+
case 'non-binary':
|
|
13615
|
+
return ['they/them', 'theirs'];
|
|
13616
|
+
default:
|
|
13617
|
+
return ['unknown'];
|
|
13618
|
+
}
|
|
13619
|
+
}
|
|
13620
|
+
|
|
13621
|
+
function extractContextInsights(emailContext) {
|
|
13622
|
+
if (!emailContext) return [];
|
|
13623
|
+
|
|
13624
|
+
const insights = [];
|
|
13625
|
+
const context = emailContext.toLowerCase();
|
|
13626
|
+
|
|
13627
|
+
// Extract potential job role hints
|
|
13628
|
+
const rolePatterns = {
|
|
13629
|
+
manager: ['manage', 'supervise', 'team lead', 'department head'],
|
|
13630
|
+
engineer: ['engineer', 'developer', 'programmer', 'software'],
|
|
13631
|
+
sales: ['sales', 'account executive', 'business development'],
|
|
13632
|
+
marketing: ['marketing', 'campaign', 'brand', 'social media'],
|
|
13633
|
+
executive: ['ceo', 'cto', 'cfo', 'director', 'vp', 'vice president'],
|
|
13634
|
+
};
|
|
13635
|
+
|
|
13636
|
+
for (const [role, patterns] of Object.entries(rolePatterns)) {
|
|
13637
|
+
if (patterns.some((pattern) => context.includes(pattern))) {
|
|
13638
|
+
insights.push(`Possible ${role} role indicated in email`);
|
|
13639
|
+
}
|
|
13640
|
+
}
|
|
13641
|
+
|
|
13642
|
+
// Extract company hints
|
|
13643
|
+
const companyMatch = context.match(/(?:at|from|of)\s+([A-Z][A-Za-z0-9\s&]+)(?:\s|$)/);
|
|
13644
|
+
if (companyMatch && companyMatch[1].length > 2) {
|
|
13645
|
+
insights.push(`Mentioned company/organization: ${companyMatch[1].trim()}`);
|
|
13646
|
+
}
|
|
13647
|
+
|
|
13648
|
+
// Extract project/technology hints
|
|
13649
|
+
const techMatch = context.match(/(?:using|with|built\s+in)\s+([A-Za-z0-9\s+#]+)(?:\s|$)/);
|
|
13650
|
+
if (techMatch) {
|
|
13651
|
+
insights.push(`Technology mentioned: ${techMatch[1].trim()}`);
|
|
13652
|
+
}
|
|
13653
|
+
|
|
13654
|
+
// Extract urgency/priority
|
|
13655
|
+
if (context.includes('urgent') || context.includes('asap') || context.includes('immediately')) {
|
|
13656
|
+
insights.push('Email suggests urgency or time sensitivity');
|
|
13657
|
+
}
|
|
13658
|
+
|
|
13659
|
+
// Extract tone
|
|
13660
|
+
if (context.includes('thank you') || context.includes('appreciate') || context.includes('grateful')) {
|
|
13661
|
+
insights.push('Email shows appreciative/grateful tone');
|
|
13662
|
+
}
|
|
13663
|
+
|
|
13664
|
+
return insights.slice(0, 5); // Limit to 5 insights
|
|
13665
|
+
}
|
|
13666
|
+
|
|
13667
|
+
// Existing helper functions (kept from previous version)
|
|
13668
|
+
function validateURL(url) {
|
|
13669
|
+
if (!url || url.toLowerCase() === 'not available' || url.trim() === '') {
|
|
13670
|
+
return 'Not available';
|
|
13671
|
+
}
|
|
13672
|
+
|
|
13673
|
+
const trimmed = url.trim();
|
|
13674
|
+
|
|
13675
|
+
// Ensure URL has proper protocol
|
|
13676
|
+
if (!trimmed.startsWith('http://') && !trimmed.startsWith('https://')) {
|
|
13677
|
+
// Check if it looks like a domain
|
|
13678
|
+
if (trimmed.includes('.') && !trimmed.includes(' ')) {
|
|
13679
|
+
return `https://${trimmed.replace(/^www\./, '')}`;
|
|
13680
|
+
}
|
|
13681
|
+
return 'Not available';
|
|
13682
|
+
}
|
|
13683
|
+
|
|
13684
|
+
return trimmed;
|
|
13685
|
+
}
|
|
13686
|
+
|
|
13687
|
+
function validatePhoneNumber(phone) {
|
|
13688
|
+
if (!phone || phone.toLowerCase() === 'not available' || phone.trim() === '') {
|
|
13689
|
+
return 'Not available';
|
|
13690
|
+
}
|
|
13691
|
+
|
|
13692
|
+
const cleaned = phone.replace(/[^\d\+\(\)\-\s]/g, '');
|
|
13693
|
+
const digitCount = (cleaned.match(/\d/g) || []).length;
|
|
13694
|
+
|
|
13695
|
+
// Require at least 7 digits for a valid phone number
|
|
13696
|
+
if (digitCount < 7) {
|
|
13697
|
+
return 'Not available';
|
|
13698
|
+
}
|
|
13699
|
+
|
|
13700
|
+
return cleaned.trim();
|
|
13701
|
+
}
|
|
13702
|
+
|
|
13703
|
+
function cleanText(text) {
|
|
13704
|
+
if (!text || text.toLowerCase() === 'not available' || text.trim() === '') {
|
|
13705
|
+
return 'Not available';
|
|
13706
|
+
}
|
|
13707
|
+
|
|
13708
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
13709
|
+
}
|
|
13710
|
+
|
|
13711
|
+
function cleanStateProvince(state) {
|
|
13712
|
+
if (!state || state.toLowerCase() === 'not available' || state.trim() === '') {
|
|
13713
|
+
return 'Not available';
|
|
13714
|
+
}
|
|
13715
|
+
|
|
13716
|
+
return state.trim();
|
|
13717
|
+
}
|
|
13718
|
+
|
|
13719
|
+
function ensureArray(value) {
|
|
13720
|
+
if (!value) return [];
|
|
13721
|
+
if (Array.isArray(value)) return value;
|
|
13722
|
+
if (typeof value === 'string') {
|
|
13723
|
+
try {
|
|
13724
|
+
const parsed = JSON.parse(value);
|
|
13725
|
+
if (Array.isArray(parsed)) return parsed;
|
|
13726
|
+
} catch (e) {
|
|
13727
|
+
// If it's a comma-separated string, split it
|
|
13728
|
+
if (value.includes(',')) {
|
|
13729
|
+
return value
|
|
13730
|
+
.split(',')
|
|
13731
|
+
.map((item) => item.trim())
|
|
13732
|
+
.filter((item) => item);
|
|
13733
|
+
}
|
|
13734
|
+
// Otherwise, return as single item array
|
|
13735
|
+
return [value.trim()];
|
|
13736
|
+
}
|
|
13737
|
+
}
|
|
13738
|
+
return [String(value)];
|
|
13739
|
+
}
|
|
13740
|
+
|
|
13741
|
+
function getInitials(firstName, lastName) {
|
|
13742
|
+
const first = firstName?.charAt(0)?.toUpperCase() || '';
|
|
13743
|
+
const last = lastName?.charAt(0)?.toUpperCase() || '';
|
|
13744
|
+
return first + last || '?';
|
|
13745
|
+
}
|
|
13746
|
+
|
|
13747
|
+
function calculateSeniorityScore(data) {
|
|
13748
|
+
let score = 0;
|
|
13749
|
+
|
|
13750
|
+
// Add points for career level
|
|
13751
|
+
const levelScores = {
|
|
13752
|
+
executive: 90,
|
|
13753
|
+
director: 80,
|
|
13754
|
+
lead: 70,
|
|
13755
|
+
senior: 60,
|
|
13756
|
+
mid: 40,
|
|
13757
|
+
entry: 20,
|
|
13758
|
+
unknown: 30,
|
|
13759
|
+
};
|
|
13760
|
+
|
|
13761
|
+
score += levelScores[data.person_career_level?.toLowerCase()] || 30;
|
|
13762
|
+
|
|
13763
|
+
// Add points for years of experience
|
|
13764
|
+
if (data.person_years_experience) {
|
|
13765
|
+
const exp = parseInt(data.person_years_experience) || 0;
|
|
13766
|
+
score += Math.min(exp * 2, 40); // Max 40 points for 20+ years
|
|
13767
|
+
}
|
|
13768
|
+
|
|
13769
|
+
// Add points for title keywords
|
|
13770
|
+
const title = data.person_title?.toLowerCase() || '';
|
|
13771
|
+
if (title.includes('vp') || title.includes('vice president') || title.includes('chief')) score += 20;
|
|
13772
|
+
if (title.includes('director')) score += 15;
|
|
13773
|
+
if (title.includes('manager') || title.includes('lead') || title.includes('head')) score += 10;
|
|
13774
|
+
if (title.includes('senior')) score += 5;
|
|
13775
|
+
|
|
13776
|
+
return Math.min(100, score);
|
|
13777
|
+
}
|
|
13778
|
+
|
|
13779
|
+
function getDefaultPersonInfo(name, email, emailContext = '', error = null) {
|
|
13780
|
+
const nameParts = name.trim().split(/\s+/);
|
|
13781
|
+
const firstName = nameParts[0] || '';
|
|
13782
|
+
const lastName = nameParts.slice(1).join(' ') || '';
|
|
13783
|
+
const domain = email.includes('@') ? email.split('@')[1] : '';
|
|
13784
|
+
const companyGuess = domain
|
|
13785
|
+
.replace(/\..*$/, '')
|
|
13786
|
+
.replace(/[^a-z]/gi, ' ')
|
|
13787
|
+
.replace(/\b\w/g, (l) => l.toUpperCase());
|
|
13788
|
+
|
|
13789
|
+
// Extract basic insights from email context
|
|
13790
|
+
const contextInsights = emailContext ? extractContextInsights(emailContext) : [];
|
|
13791
|
+
|
|
13792
|
+
return {
|
|
13793
|
+
// Basic Information
|
|
13794
|
+
person_full_name: name,
|
|
13795
|
+
person_first_name: firstName,
|
|
13796
|
+
person_last_name: lastName,
|
|
13797
|
+
person_middle_name: '',
|
|
13798
|
+
person_preferred_name: '',
|
|
13799
|
+
|
|
13800
|
+
// Demographic Information
|
|
13801
|
+
person_age: 'unknown',
|
|
13802
|
+
person_gender: 'unknown',
|
|
13803
|
+
person_nationality: 'unknown',
|
|
13804
|
+
person_age_range: 'Unknown',
|
|
13805
|
+
person_pronouns: ['unknown'],
|
|
13806
|
+
|
|
13807
|
+
// Professional Information
|
|
13808
|
+
person_title: 'Professional',
|
|
13809
|
+
person_company: companyGuess || 'Unknown',
|
|
13810
|
+
person_industry: 'Technology',
|
|
13811
|
+
person_bio: 'Information not available',
|
|
13812
|
+
|
|
13813
|
+
// Location
|
|
13814
|
+
person_location_city: 'Not available',
|
|
13815
|
+
person_location_state: 'Not available',
|
|
13816
|
+
person_location_country: 'unknown',
|
|
13817
|
+
person_location_full: 'Not available',
|
|
13818
|
+
|
|
13819
|
+
// Contact Information
|
|
13820
|
+
person_phone: 'Not available',
|
|
13821
|
+
person_email: email,
|
|
13822
|
+
person_email_alternate: '',
|
|
13823
|
+
|
|
13824
|
+
// Education
|
|
13825
|
+
person_education: [],
|
|
13826
|
+
person_education_highest: 'Not available',
|
|
13827
|
+
|
|
13828
|
+
// Professional Details
|
|
13829
|
+
person_skills: [],
|
|
13830
|
+
person_expertise: [],
|
|
13831
|
+
person_career_level: 'unknown',
|
|
13832
|
+
person_years_experience: 0,
|
|
13833
|
+
|
|
13834
|
+
// Social & Online Presence
|
|
13835
|
+
person_linkedin: 'Not available',
|
|
13836
|
+
person_twitter: 'Not available',
|
|
13837
|
+
person_github: 'Not available',
|
|
13838
|
+
person_website: 'Not available',
|
|
13839
|
+
person_social_other: [],
|
|
13840
|
+
|
|
13841
|
+
// Additional Information
|
|
13842
|
+
person_languages: ['English'],
|
|
13843
|
+
person_interests: [],
|
|
13844
|
+
person_achievements: [],
|
|
13845
|
+
person_current_projects: [],
|
|
13846
|
+
person_context_insights: contextInsights,
|
|
13847
|
+
|
|
13848
|
+
// Metadata
|
|
13849
|
+
person_available_for_opportunities: false,
|
|
13850
|
+
person_last_updated: new Date().toISOString(),
|
|
13851
|
+
person_source_confidence: 0,
|
|
13852
|
+
person_has_context_hints: contextInsights.length > 0,
|
|
13853
|
+
|
|
13854
|
+
// Processed fields
|
|
13855
|
+
person_name_initials: getInitials(firstName, lastName),
|
|
13856
|
+
person_seniority_score: 0,
|
|
13857
|
+
|
|
13858
|
+
// Structured objects
|
|
13859
|
+
person_demographics: {
|
|
13860
|
+
age: 'unknown',
|
|
13861
|
+
age_range: 'Unknown',
|
|
13862
|
+
gender: 'unknown',
|
|
13863
|
+
pronouns: ['unknown'],
|
|
13864
|
+
nationality: 'unknown',
|
|
13865
|
+
nationality_name: 'Unknown',
|
|
13866
|
+
},
|
|
13867
|
+
|
|
13868
|
+
person_contact: {
|
|
13869
|
+
email: email,
|
|
13870
|
+
phone: 'Not available',
|
|
13871
|
+
location: {
|
|
13872
|
+
city: 'Not available',
|
|
13873
|
+
state: 'Not available',
|
|
13874
|
+
country: 'unknown',
|
|
13875
|
+
country_name: 'Unknown',
|
|
13876
|
+
full: 'Not available',
|
|
13877
|
+
},
|
|
13878
|
+
},
|
|
13879
|
+
|
|
13880
|
+
person_professional: {
|
|
13881
|
+
title: 'Professional',
|
|
13882
|
+
company: companyGuess || 'Unknown',
|
|
13883
|
+
industry: 'Technology',
|
|
13884
|
+
career_level: 'unknown',
|
|
13885
|
+
years_experience: 0,
|
|
13886
|
+
skills: [],
|
|
13887
|
+
expertise: [],
|
|
13888
|
+
education: [],
|
|
13889
|
+
},
|
|
13890
|
+
|
|
13891
|
+
person_online: {
|
|
13892
|
+
linkedin: 'Not available',
|
|
13893
|
+
twitter: 'Not available',
|
|
13894
|
+
github: 'Not available',
|
|
13895
|
+
website: 'Not available',
|
|
13896
|
+
other: [],
|
|
13897
|
+
},
|
|
13898
|
+
|
|
13899
|
+
context_analysis: {
|
|
13900
|
+
has_context: !!emailContext,
|
|
13901
|
+
context_length: emailContext?.length || 0,
|
|
13902
|
+
extracted_insights: contextInsights,
|
|
13903
|
+
context_useful: contextInsights.length > 0,
|
|
13904
|
+
},
|
|
13905
|
+
|
|
13906
|
+
retrieved_at: new Date().toISOString(),
|
|
13907
|
+
error: error || 'API call failed',
|
|
13908
|
+
};
|
|
13909
|
+
}
|
|
13910
|
+
|
|
13911
|
+
// Build context-aware prompt
|
|
13912
|
+
let context_prompt = `Research the person: "${name}" (${email}).`;
|
|
13913
|
+
|
|
13914
|
+
if (email_context) {
|
|
13915
|
+
context_prompt += `\n\nEMAIL CONTEXT PROVIDED:\n"${email_context.substring(0, 500)}${email_context.length > 500 ? '...' : ''}"`;
|
|
13916
|
+
}
|
|
13917
|
+
|
|
13918
|
+
context_prompt += `
|
|
13919
|
+
|
|
13920
|
+
REQUIRED PERSON INFORMATION:
|
|
13921
|
+
1. Full name (first, middle, last)
|
|
13922
|
+
2. Professional title/role
|
|
13923
|
+
3. Company/organization they work for
|
|
13924
|
+
4. Industry/field they work in
|
|
13925
|
+
5. Location (city, state, country)
|
|
13926
|
+
6. Professional biography (1-2 sentences)
|
|
13927
|
+
7. Education background
|
|
13928
|
+
8. Professional skills/expertise
|
|
13929
|
+
9. Social media profiles (LinkedIn, Twitter, GitHub, etc.)
|
|
13930
|
+
10. Personal website or portfolio
|
|
13931
|
+
|
|
13932
|
+
DEMOGRAPHIC INFORMATION:
|
|
13933
|
+
11. Estimated age (based on career, education, and public information)
|
|
13934
|
+
12. Gender (based on name, pronouns in content, or public profiles)
|
|
13935
|
+
13. Nationality/country of origin (if discernible) (based on name, pronouns in content, or public profiles)
|
|
13936
|
+
|
|
13937
|
+
CONTACT INFORMATION:
|
|
13938
|
+
14. Phone number (if available)
|
|
13939
|
+
15. Alternate email addresses (if available)
|
|
13940
|
+
16. Physical address (if available and appropriate)
|
|
13941
|
+
|
|
13942
|
+
ADDITIONAL INSIGHTS:
|
|
13943
|
+
17. Career level (entry, mid, senior, executive, etc.)
|
|
13944
|
+
18. Years of experience
|
|
13945
|
+
19. Notable achievements/awards
|
|
13946
|
+
20. Languages spoken
|
|
13947
|
+
21. Professional interests
|
|
13948
|
+
22. Current projects
|
|
13949
|
+
23. Availability for opportunities
|
|
13950
|
+
|
|
13951
|
+
INSTRUCTIONS:
|
|
13952
|
+
- Use web search to find accurate, public information
|
|
13953
|
+
- Use email context provided to infer additional details
|
|
13954
|
+
- Respect privacy - only include publicly available information
|
|
13955
|
+
- For age: provide numeric estimate or range (e.g., 35, 30-40, unknown)
|
|
13956
|
+
- For gender: use male/female/non-binary/unknown based on available cues
|
|
13957
|
+
- For country: use 2-letter ISO code (e.g., US, IN, UK)
|
|
13958
|
+
- If information is not available, use "Not available" or appropriate defaults
|
|
13959
|
+
- For location, use standard formats (e.g., "San Francisco, CA, USA")
|
|
13960
|
+
- For social media, provide full URLs when available
|
|
13961
|
+
- Focus on professional information suitable for business networking
|
|
13962
|
+
|
|
13963
|
+
Be thorough but respectful of privacy boundaries.`;
|
|
13964
|
+
|
|
13965
|
+
const person_info_ret = await submit_chat_gpt_prompt({
|
|
13966
|
+
uid,
|
|
13967
|
+
prompt: context_prompt,
|
|
13968
|
+
model: 'gpt-5-nano',
|
|
13969
|
+
response_format: z.object({
|
|
13970
|
+
// Basic Information
|
|
13971
|
+
person_full_name: z.string().describe("Person's full name"),
|
|
13972
|
+
person_first_name: z.string().describe('First name'),
|
|
13973
|
+
person_last_name: z.string().describe('Last name'),
|
|
13974
|
+
person_middle_name: z.string().optional().nullable().describe('Middle name if available'),
|
|
13975
|
+
person_preferred_name: z.string().optional().nullable().describe('Preferred/nickname if different'),
|
|
13976
|
+
|
|
13977
|
+
// Demographic Information
|
|
13978
|
+
person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('find age base on the name Estimated age or range'),
|
|
13979
|
+
person_gender: z.enum(['male', 'female']).describe('find Gender based on the name'),
|
|
13980
|
+
person_nationality: z.string().describe('find nationality based on the name Country of origin/nationality (2-letter ISO code)'),
|
|
13981
|
+
|
|
13982
|
+
// Professional Information
|
|
13983
|
+
person_title: z.string().describe('Professional title/role'),
|
|
13984
|
+
person_company: z.string().describe('Current company/organization'),
|
|
13985
|
+
person_industry: z.string().describe('Primary industry/field'),
|
|
13986
|
+
person_bio: z.string().describe('Professional biography (1-2 sentences)'),
|
|
13987
|
+
|
|
13988
|
+
// Location
|
|
13989
|
+
person_location_city: z.string().describe('City of residence/work'),
|
|
13990
|
+
person_location_state: z.string().describe('State/province'),
|
|
13991
|
+
person_location_country: z.string().describe('Country (2-letter ISO code)'),
|
|
13992
|
+
person_location_full: z.string().describe('Full location string'),
|
|
13993
|
+
|
|
13994
|
+
// Contact Information
|
|
13995
|
+
person_phone: z.string().describe("Phone number or 'Not available'"),
|
|
13996
|
+
person_email: z.string().describe('Primary email address'),
|
|
13997
|
+
person_email_alternate: z.string().optional().nullable().describe('Alternate email if available'),
|
|
13998
|
+
|
|
13999
|
+
// Education
|
|
14000
|
+
person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
|
|
14001
|
+
person_education_highest: z.string().describe('Highest degree obtained'),
|
|
14002
|
+
|
|
14003
|
+
// Professional Details
|
|
14004
|
+
person_skills: z.array(z.string()).describe('Array of professional skills'),
|
|
14005
|
+
person_expertise: z.array(z.string()).describe('Areas of expertise'),
|
|
14006
|
+
person_career_level: z.enum(['entry', 'mid', 'senior', 'lead', 'manager', 'director', 'executive', 'founder', 'unknown']),
|
|
14007
|
+
person_years_experience: z.number().int().min(0).max(60).describe('Years of professional experience'),
|
|
14008
|
+
|
|
14009
|
+
// Social & Online Presence
|
|
14010
|
+
person_linkedin: z.string().describe("LinkedIn profile URL or 'Not available'"),
|
|
14011
|
+
person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
|
|
14012
|
+
person_github: z.string().describe("GitHub profile URL or 'Not available'"),
|
|
14013
|
+
person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
|
|
14014
|
+
person_social_other: z.array(z.string()).optional().nullable().describe('Other social media profiles'),
|
|
14015
|
+
|
|
14016
|
+
// Additional Information
|
|
14017
|
+
person_languages: z.array(z.string()).describe('Languages spoken'),
|
|
14018
|
+
person_interests: z.array(z.string()).describe('Professional/personal interests'),
|
|
14019
|
+
person_achievements: z.array(z.string()).optional().nullable().describe('Notable achievements/awards'),
|
|
14020
|
+
person_current_projects: z.array(z.string()).optional().nullable().describe('Current projects'),
|
|
14021
|
+
|
|
14022
|
+
// Context Extracted Information
|
|
14023
|
+
person_context_insights: z.array(z.string()).optional().nullable().describe('Insights extracted from provided email context'),
|
|
14024
|
+
|
|
14025
|
+
// Metadata
|
|
14026
|
+
person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
|
|
14027
|
+
person_last_updated: z.string().describe('Date information was last verified'),
|
|
14028
|
+
person_source_confidence: z.number().min(0).max(100).describe('Confidence score for information accuracy'),
|
|
14029
|
+
person_has_context_hints: z.boolean().describe('Whether email context provided useful hints'),
|
|
14030
|
+
}),
|
|
14031
|
+
metadata: {
|
|
14032
|
+
func: 'get_person_info',
|
|
14033
|
+
person_name: name,
|
|
14034
|
+
person_email: email,
|
|
14035
|
+
has_email_context: !!email_context,
|
|
14036
|
+
email_context_length: email_context?.length || 0,
|
|
14037
|
+
context_hash: email_context ? await hashString(email_context.substring(0, 200)) : null,
|
|
14038
|
+
},
|
|
14039
|
+
account_profile_info,
|
|
14040
|
+
tools: [{ type: 'web_search_preview' }],
|
|
14041
|
+
});
|
|
14042
|
+
|
|
14043
|
+
try {
|
|
14044
|
+
if (person_info_ret.code > -1) {
|
|
14045
|
+
const data = typeof person_info_ret.data === 'string' ? JSON.parse(person_info_ret.data) : person_info_ret.data;
|
|
14046
|
+
|
|
14047
|
+
// Process and enhance the data
|
|
14048
|
+
const processedData = processPersonInfo(data, name, email, email_context);
|
|
14049
|
+
|
|
14050
|
+
return processedData;
|
|
14051
|
+
} else {
|
|
14052
|
+
console.error('Person info API call failed:', person_info_ret);
|
|
14053
|
+
return getDefaultPersonInfo(name, email, email_context);
|
|
14054
|
+
}
|
|
14055
|
+
} catch (error) {
|
|
14056
|
+
console.error('Error in get_person_info:', error);
|
|
14057
|
+
return getDefaultPersonInfo(name, email, email_context, error.message);
|
|
14058
|
+
}
|
|
14059
|
+
};
|