@xuda.io/ai_module 1.1.4679 → 1.1.4680
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 +630 -0
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -9729,3 +9729,633 @@ export const get_person_info = async function (uid, name, email, account_profile
|
|
|
9729
9729
|
return getDefaultPersonInfo(name, email, email_context, error.message);
|
|
9730
9730
|
}
|
|
9731
9731
|
};
|
|
9732
|
+
|
|
9733
|
+
export const analyze_email_account_type = async function (uid, name, email, account_profile_info, email_context = '') {
|
|
9734
|
+
// ========== HELPER FUNCTIONS ==========
|
|
9735
|
+
|
|
9736
|
+
function enhanceEmailAnalysis(data, name, email, emailContext) {
|
|
9737
|
+
const domain = email.split('@')[1] || '';
|
|
9738
|
+
const localPart = email.split('@')[0] || '';
|
|
9739
|
+
const nameParts = name.toLowerCase().split(/\s+/);
|
|
9740
|
+
const firstName = nameParts[0] || '';
|
|
9741
|
+
const lastName = nameParts.slice(-1)[0] || '';
|
|
9742
|
+
|
|
9743
|
+
// Enhanced business company detection
|
|
9744
|
+
if (data.account_type === 'business' && !data.business_company_name) {
|
|
9745
|
+
data.business_company_name = inferCompanyFromDomain(domain);
|
|
9746
|
+
}
|
|
9747
|
+
|
|
9748
|
+
// Enhanced person detection
|
|
9749
|
+
if (!data.is_real_person) {
|
|
9750
|
+
data.is_real_person = detectRealPerson(localPart, name, domain);
|
|
9751
|
+
}
|
|
9752
|
+
|
|
9753
|
+
if (!data.person_name_in_email) {
|
|
9754
|
+
data.person_name_in_email = checkNameInEmail(localPart, firstName, lastName);
|
|
9755
|
+
}
|
|
9756
|
+
|
|
9757
|
+
// Enhanced pattern analysis
|
|
9758
|
+
if (data.email_pattern === 'unknown') {
|
|
9759
|
+
data.email_pattern = analyzeEmailPattern(localPart, domain);
|
|
9760
|
+
}
|
|
9761
|
+
|
|
9762
|
+
// Enhanced personal provider detection
|
|
9763
|
+
if (data.account_type === 'personal' && !data.personal_account_provider) {
|
|
9764
|
+
data.personal_account_provider = identifyPersonalProvider(domain);
|
|
9765
|
+
}
|
|
9766
|
+
|
|
9767
|
+
// Enhanced risk assessment
|
|
9768
|
+
if (data.spam_risk_level === 'unknown') {
|
|
9769
|
+
data.spam_risk_level = assessSpamRisk(localPart, domain, data.account_type);
|
|
9770
|
+
}
|
|
9771
|
+
|
|
9772
|
+
// Enhanced activity assessment
|
|
9773
|
+
if (!data.is_likely_active) {
|
|
9774
|
+
data.is_likely_active = assessEmailActivity(localPart, domain, data.account_type, data.business_account_category);
|
|
9775
|
+
}
|
|
9776
|
+
|
|
9777
|
+
// Add domain analysis
|
|
9778
|
+
data.domain_analysis = {
|
|
9779
|
+
domain: domain,
|
|
9780
|
+
is_public_provider: isPublicEmailProvider(domain),
|
|
9781
|
+
is_custom_domain: isCustomDomain(domain),
|
|
9782
|
+
is_free_email: isFreeEmailDomain(domain),
|
|
9783
|
+
is_education: isEducationDomain(domain),
|
|
9784
|
+
is_government: isGovernmentDomain(domain),
|
|
9785
|
+
tld: domain.split('.').pop() || '',
|
|
9786
|
+
};
|
|
9787
|
+
|
|
9788
|
+
// Add name-email correlation score
|
|
9789
|
+
data.name_email_correlation = calculateNameEmailCorrelation(localPart, firstName, lastName);
|
|
9790
|
+
|
|
9791
|
+
// Add professional score
|
|
9792
|
+
data.professional_score = calculateProfessionalScore(data);
|
|
9793
|
+
|
|
9794
|
+
// Add structured recommendations
|
|
9795
|
+
data.recommendations = generateRecommendations(data);
|
|
9796
|
+
|
|
9797
|
+
// Add verification suggestions
|
|
9798
|
+
data.verification_suggestions = generateVerificationSuggestions(data, emailContext);
|
|
9799
|
+
|
|
9800
|
+
// Add timestamp
|
|
9801
|
+
data.analysis_timestamp = data.analysis_timestamp || new Date().toISOString();
|
|
9802
|
+
|
|
9803
|
+
return data;
|
|
9804
|
+
}
|
|
9805
|
+
|
|
9806
|
+
function inferCompanyFromDomain(domain) {
|
|
9807
|
+
if (!domain) return 'Unknown';
|
|
9808
|
+
|
|
9809
|
+
// Remove common TLDs and public providers
|
|
9810
|
+
const baseDomain = domain.replace(/\.(com|org|net|co|io|ai|tech|app|dev)$/, '');
|
|
9811
|
+
|
|
9812
|
+
// Remove common subdomains
|
|
9813
|
+
const cleanDomain = baseDomain.replace(/^(mail\.|email\.|webmail\.|smtp\.|mx\.|imap\.)/, '').replace(/\.(com|org|net)$/, '');
|
|
9814
|
+
|
|
9815
|
+
// Convert to readable company name
|
|
9816
|
+
const companyName = cleanDomain
|
|
9817
|
+
.split(/[\.\-]/)
|
|
9818
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
9819
|
+
.join(' ');
|
|
9820
|
+
|
|
9821
|
+
return companyName || 'Unknown';
|
|
9822
|
+
}
|
|
9823
|
+
|
|
9824
|
+
function detectRealPerson(localPart, name, domain) {
|
|
9825
|
+
// Check for generic email addresses
|
|
9826
|
+
const genericPatterns = [
|
|
9827
|
+
'info',
|
|
9828
|
+
'support',
|
|
9829
|
+
'sales',
|
|
9830
|
+
'contact',
|
|
9831
|
+
'hello',
|
|
9832
|
+
'noreply',
|
|
9833
|
+
'no-reply',
|
|
9834
|
+
'admin',
|
|
9835
|
+
'administrator',
|
|
9836
|
+
'webmaster',
|
|
9837
|
+
'postmaster',
|
|
9838
|
+
'hostmaster',
|
|
9839
|
+
'abuse',
|
|
9840
|
+
'security',
|
|
9841
|
+
'billing',
|
|
9842
|
+
'accounts',
|
|
9843
|
+
'finance',
|
|
9844
|
+
'hr',
|
|
9845
|
+
'humanresources',
|
|
9846
|
+
'marketing',
|
|
9847
|
+
'pr',
|
|
9848
|
+
'press',
|
|
9849
|
+
'media',
|
|
9850
|
+
'legal',
|
|
9851
|
+
'it',
|
|
9852
|
+
'techsupport',
|
|
9853
|
+
'help',
|
|
9854
|
+
'service',
|
|
9855
|
+
'customerservice',
|
|
9856
|
+
'feedback',
|
|
9857
|
+
'inquiries',
|
|
9858
|
+
];
|
|
9859
|
+
|
|
9860
|
+
const localLower = localPart.toLowerCase();
|
|
9861
|
+
|
|
9862
|
+
// If it matches generic patterns, likely not a real person
|
|
9863
|
+
if (genericPatterns.some((pattern) => localLower === pattern || localLower.startsWith(pattern + '.') || localLower.endsWith('.' + pattern))) {
|
|
9864
|
+
return false;
|
|
9865
|
+
}
|
|
9866
|
+
|
|
9867
|
+
// Check for role-based patterns
|
|
9868
|
+
const rolePatterns = ['ceo', 'cto', 'cfo', 'coo', 'cm', 'director', 'manager', 'head', 'lead', 'senior', 'junior', 'associate', 'analyst', 'engineer', 'developer', 'designer', 'architect', 'consultant', 'advisor'];
|
|
9869
|
+
|
|
9870
|
+
// If contains role patterns but not name, might be generic
|
|
9871
|
+
const hasRolePattern = rolePatterns.some((role) => localLower.includes(role));
|
|
9872
|
+
const hasNameElements = checkNameInEmail(localPart, name.toLowerCase().split(' ')[0], name.toLowerCase().split(' ').slice(-1)[0]);
|
|
9873
|
+
|
|
9874
|
+
if (hasRolePattern && !hasNameElements) {
|
|
9875
|
+
return false;
|
|
9876
|
+
}
|
|
9877
|
+
|
|
9878
|
+
// Check for numeric-only or random strings
|
|
9879
|
+
if (/^\d+$/.test(localPart) || /^[a-f0-9]{32}$/.test(localPart)) {
|
|
9880
|
+
return false;
|
|
9881
|
+
}
|
|
9882
|
+
|
|
9883
|
+
// Check for obvious spam patterns
|
|
9884
|
+
if (localLower.includes('spam') || localLower.includes('bot') || localLower.includes('test')) {
|
|
9885
|
+
return false;
|
|
9886
|
+
}
|
|
9887
|
+
|
|
9888
|
+
return true;
|
|
9889
|
+
}
|
|
9890
|
+
|
|
9891
|
+
function checkNameInEmail(localPart, firstName, lastName) {
|
|
9892
|
+
if (!firstName && !lastName) return false;
|
|
9893
|
+
|
|
9894
|
+
const localLower = localPart.toLowerCase();
|
|
9895
|
+
const firstLower = firstName.toLowerCase();
|
|
9896
|
+
const lastLower = lastName.toLowerCase();
|
|
9897
|
+
|
|
9898
|
+
// Check various name patterns
|
|
9899
|
+
const patterns = [
|
|
9900
|
+
`${firstLower}.${lastLower}`, // john.doe
|
|
9901
|
+
`${firstLower}${lastLower}`, // johndoe
|
|
9902
|
+
`${firstLower.charAt(0)}${lastLower}`, // jdoe
|
|
9903
|
+
`${firstLower}_${lastLower}`, // john_doe
|
|
9904
|
+
`${firstLower}-${lastLower}`, // john-doe
|
|
9905
|
+
`${lastLower}.${firstLower}`, // doe.john
|
|
9906
|
+
`${firstLower.charAt(0)}.${lastLower}`, // j.doe
|
|
9907
|
+
`${lastLower}${firstLower}`, // doejohn
|
|
9908
|
+
`${firstLower}`, // john
|
|
9909
|
+
`${lastLower}`, // doe
|
|
9910
|
+
];
|
|
9911
|
+
|
|
9912
|
+
return patterns.some((pattern) => localLower === pattern || localLower.startsWith(pattern + '.') || localLower.includes('.' + pattern));
|
|
9913
|
+
}
|
|
9914
|
+
|
|
9915
|
+
function analyzeEmailPattern(localPart, domain) {
|
|
9916
|
+
const localLower = localPart.toLowerCase();
|
|
9917
|
+
|
|
9918
|
+
// Common business patterns
|
|
9919
|
+
if (/^[a-z]+\.[a-z]+$/.test(localLower)) return 'first.last@company.com';
|
|
9920
|
+
if (/^[a-z]+[a-z]+$/.test(localLower) && localLower.length > 5) return 'firstlast@company.com';
|
|
9921
|
+
if (/^[a-z]\.[a-z]+$/.test(localLower)) return 'f.last@company.com';
|
|
9922
|
+
if (/^[a-z][a-z]+$/.test(localLower) && localLower.length <= 5) return 'flast@company.com';
|
|
9923
|
+
if (/^[a-z]+$/.test(localLower) && localLower.length <= 8) return 'first@company.com';
|
|
9924
|
+
if (/^[a-z]+\.[a-z]+\.[a-z]+$/.test(localLower)) return 'first.m.last@company.com';
|
|
9925
|
+
|
|
9926
|
+
// Generic patterns
|
|
9927
|
+
if (/^(info|support|sales|contact|hello)$/.test(localLower)) return 'role@company.com';
|
|
9928
|
+
if (/^(hr|finance|marketing|it|legal)$/.test(localLower)) return 'department@company.com';
|
|
9929
|
+
|
|
9930
|
+
// Personal patterns
|
|
9931
|
+
if (isPublicEmailProvider(domain)) {
|
|
9932
|
+
if (/^[a-z]+\.[a-z]+$/.test(localLower)) return 'first.last@domain.com';
|
|
9933
|
+
if (/^[a-z]+[0-9]+$/.test(localLower)) return 'personal@provider.com';
|
|
9934
|
+
return 'custom@personal.com';
|
|
9935
|
+
}
|
|
9936
|
+
|
|
9937
|
+
return 'unknown';
|
|
9938
|
+
}
|
|
9939
|
+
|
|
9940
|
+
function identifyPersonalProvider(domain) {
|
|
9941
|
+
const providers = {
|
|
9942
|
+
'gmail.com': 'Google Gmail',
|
|
9943
|
+
'googlemail.com': 'Google Gmail',
|
|
9944
|
+
'outlook.com': 'Microsoft Outlook',
|
|
9945
|
+
'hotmail.com': 'Microsoft Hotmail',
|
|
9946
|
+
'live.com': 'Microsoft Live',
|
|
9947
|
+
'yahoo.com': 'Yahoo Mail',
|
|
9948
|
+
'ymail.com': 'Yahoo Mail',
|
|
9949
|
+
'aol.com': 'AOL Mail',
|
|
9950
|
+
'icloud.com': 'Apple iCloud',
|
|
9951
|
+
'me.com': 'Apple iCloud',
|
|
9952
|
+
'mac.com': 'Apple iCloud',
|
|
9953
|
+
'protonmail.com': 'ProtonMail',
|
|
9954
|
+
'proton.me': 'ProtonMail',
|
|
9955
|
+
'zoho.com': 'Zoho Mail',
|
|
9956
|
+
'yandex.com': 'Yandex Mail',
|
|
9957
|
+
'mail.com': 'Mail.com',
|
|
9958
|
+
'gmx.com': 'GMX Mail',
|
|
9959
|
+
};
|
|
9960
|
+
|
|
9961
|
+
return providers[domain.toLowerCase()] || 'Custom/Unknown';
|
|
9962
|
+
}
|
|
9963
|
+
|
|
9964
|
+
function isPublicEmailProvider(domain) {
|
|
9965
|
+
const publicProviders = ['gmail.com', 'googlemail.com', 'outlook.com', 'hotmail.com', 'live.com', 'yahoo.com', 'ymail.com', 'aol.com', 'icloud.com', 'me.com', 'mac.com', 'protonmail.com', 'proton.me', 'zoho.com', 'yandex.com', 'mail.com', 'gmx.com'];
|
|
9966
|
+
|
|
9967
|
+
return publicProviders.includes(domain.toLowerCase());
|
|
9968
|
+
}
|
|
9969
|
+
|
|
9970
|
+
function isFreeEmailDomain(domain) {
|
|
9971
|
+
const freeDomains = ['gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com', 'aol.com', 'mail.com', 'gmx.com', 'yandex.com', 'zoho.com'];
|
|
9972
|
+
|
|
9973
|
+
return freeDomains.includes(domain.toLowerCase());
|
|
9974
|
+
}
|
|
9975
|
+
|
|
9976
|
+
function isCustomDomain(domain) {
|
|
9977
|
+
const publicProviders = ['gmail.com', 'googlemail.com', 'outlook.com', 'hotmail.com', 'live.com', 'yahoo.com', 'ymail.com', 'aol.com', 'icloud.com', 'me.com', 'mac.com', 'protonmail.com', 'proton.me', 'zoho.com', 'yandex.com', 'mail.com', 'gmx.com', 'edu', 'gov', 'mil', 'org', 'net'];
|
|
9978
|
+
|
|
9979
|
+
return !publicProviders.some((provider) => domain.toLowerCase().includes(provider));
|
|
9980
|
+
}
|
|
9981
|
+
|
|
9982
|
+
function isEducationDomain(domain) {
|
|
9983
|
+
return domain.toLowerCase().endsWith('.edu') || domain.toLowerCase().includes('.ac.');
|
|
9984
|
+
}
|
|
9985
|
+
|
|
9986
|
+
function isGovernmentDomain(domain) {
|
|
9987
|
+
return domain.toLowerCase().endsWith('.gov') || domain.toLowerCase().endsWith('.mil');
|
|
9988
|
+
}
|
|
9989
|
+
|
|
9990
|
+
function assessSpamRisk(localPart, domain, accountType) {
|
|
9991
|
+
const localLower = localPart.toLowerCase();
|
|
9992
|
+
|
|
9993
|
+
// High risk indicators
|
|
9994
|
+
if (localLower.includes('spam') || localLower.includes('bot')) return 'high';
|
|
9995
|
+
if (/^[a-f0-9]{32}$/.test(localLower)) return 'high'; // MD5 hash-like
|
|
9996
|
+
if (/^\d+$/.test(localLower)) return 'high'; // Numbers only
|
|
9997
|
+
if (localLower.includes('temp') || localLower.includes('throwaway')) return 'high';
|
|
9998
|
+
|
|
9999
|
+
// Medium risk indicators
|
|
10000
|
+
if (localLower.includes('test') || localLower.includes('demo')) return 'medium';
|
|
10001
|
+
if (accountType === 'personal' && localLower.includes('+')) return 'medium'; // Plus addressing
|
|
10002
|
+
|
|
10003
|
+
// Check disposable email domains
|
|
10004
|
+
if (isDisposableDomain(domain)) return 'high';
|
|
10005
|
+
|
|
10006
|
+
return 'low';
|
|
10007
|
+
}
|
|
10008
|
+
|
|
10009
|
+
function isDisposableDomain(domain) {
|
|
10010
|
+
// Common disposable/temporary email domains
|
|
10011
|
+
const disposableDomains = ['tempmail.com', 'mailinator.com', 'guerrillamail.com', '10minutemail.com', 'yopmail.com', 'trashmail.com', 'dispostable.com', 'fakeinbox.com', 'throwawaymail.com', 'temp-mail.org'];
|
|
10012
|
+
|
|
10013
|
+
return disposableDomains.some((d) => domain.toLowerCase().includes(d));
|
|
10014
|
+
}
|
|
10015
|
+
|
|
10016
|
+
function assessEmailActivity(localPart, domain, accountType, businessCategory) {
|
|
10017
|
+
// Generic/role emails are usually monitored
|
|
10018
|
+
if (accountType === 'business' && ['generic_role', 'department', 'catch_all'].includes(businessCategory)) {
|
|
10019
|
+
return true;
|
|
10020
|
+
}
|
|
10021
|
+
|
|
10022
|
+
// Personal emails with name patterns are likely active
|
|
10023
|
+
if (accountType === 'personal' && /^[a-z]+\.[a-z]+$/.test(localPart.toLowerCase())) {
|
|
10024
|
+
return true;
|
|
10025
|
+
}
|
|
10026
|
+
|
|
10027
|
+
// Education and government emails are usually active
|
|
10028
|
+
if (isEducationDomain(domain) || isGovernmentDomain(domain)) {
|
|
10029
|
+
return true;
|
|
10030
|
+
}
|
|
10031
|
+
|
|
10032
|
+
// Disposable domains are usually inactive after short period
|
|
10033
|
+
if (isDisposableDomain(domain)) {
|
|
10034
|
+
return false;
|
|
10035
|
+
}
|
|
10036
|
+
|
|
10037
|
+
// Default assumption
|
|
10038
|
+
return true;
|
|
10039
|
+
}
|
|
10040
|
+
|
|
10041
|
+
function calculateNameEmailCorrelation(localPart, firstName, lastName) {
|
|
10042
|
+
if (!firstName && !lastName) return 0;
|
|
10043
|
+
|
|
10044
|
+
let score = 0;
|
|
10045
|
+
const localLower = localPart.toLowerCase();
|
|
10046
|
+
const firstLower = firstName.toLowerCase();
|
|
10047
|
+
const lastLower = lastName.toLowerCase();
|
|
10048
|
+
|
|
10049
|
+
// Exact name patterns
|
|
10050
|
+
if (localLower === `${firstLower}.${lastLower}`) score += 40;
|
|
10051
|
+
if (localLower === `${firstLower}${lastLower}`) score += 35;
|
|
10052
|
+
if (localLower === `${firstLower.charAt(0)}${lastLower}`) score += 30;
|
|
10053
|
+
if (localLower === `${firstLower}_${lastLower}`) score += 25;
|
|
10054
|
+
|
|
10055
|
+
// Partial matches
|
|
10056
|
+
if (localLower.includes(firstLower)) score += 15;
|
|
10057
|
+
if (localLower.includes(lastLower)) score += 15;
|
|
10058
|
+
|
|
10059
|
+
// Initial matches
|
|
10060
|
+
if (localLower.startsWith(firstLower.charAt(0))) score += 10;
|
|
10061
|
+
|
|
10062
|
+
// Length consideration (shorter emails with names are better)
|
|
10063
|
+
if (localLower.length <= 20) score += 5;
|
|
10064
|
+
|
|
10065
|
+
return Math.min(score, 100);
|
|
10066
|
+
}
|
|
10067
|
+
|
|
10068
|
+
function calculateProfessionalScore(data) {
|
|
10069
|
+
let score = 50; // Base score
|
|
10070
|
+
|
|
10071
|
+
// Account type weights
|
|
10072
|
+
if (data.account_type === 'business') score += 30;
|
|
10073
|
+
else if (data.account_type === 'personal') score += 10;
|
|
10074
|
+
|
|
10075
|
+
// Real person bonus
|
|
10076
|
+
if (data.is_real_person) score += 20;
|
|
10077
|
+
|
|
10078
|
+
// Active email bonus
|
|
10079
|
+
if (data.is_likely_active) score += 15;
|
|
10080
|
+
|
|
10081
|
+
// Low spam risk bonus
|
|
10082
|
+
if (data.spam_risk_level === 'low') score += 10;
|
|
10083
|
+
else if (data.spam_risk_level === 'high') score -= 20;
|
|
10084
|
+
|
|
10085
|
+
// Name-email correlation bonus
|
|
10086
|
+
score += (data.name_email_correlation / 100) * 20;
|
|
10087
|
+
|
|
10088
|
+
// Business category bonus
|
|
10089
|
+
if (data.business_account_category === 'personal_employee') score += 15;
|
|
10090
|
+
|
|
10091
|
+
return Math.max(0, Math.min(100, score));
|
|
10092
|
+
}
|
|
10093
|
+
|
|
10094
|
+
function generateRecommendations(data) {
|
|
10095
|
+
const recommendations = [];
|
|
10096
|
+
|
|
10097
|
+
if (data.account_type === 'business' && data.is_real_person) {
|
|
10098
|
+
if (data.business_account_category === 'personal_employee') {
|
|
10099
|
+
recommendations.push('Ideal for direct professional outreach');
|
|
10100
|
+
recommendations.push('Suitable for sales, recruitment, and partnership discussions');
|
|
10101
|
+
} else if (data.business_account_category === 'department') {
|
|
10102
|
+
recommendations.push('Best for department-specific inquiries');
|
|
10103
|
+
recommendations.push('Use for customer support or service requests');
|
|
10104
|
+
}
|
|
10105
|
+
} else if (data.account_type === 'personal') {
|
|
10106
|
+
recommendations.push('Suitable for networking and personal connections');
|
|
10107
|
+
recommendations.push('May be used for freelance or consulting work');
|
|
10108
|
+
}
|
|
10109
|
+
|
|
10110
|
+
if (data.spam_risk_level === 'high') {
|
|
10111
|
+
recommendations.push('Consider verifying before important communications');
|
|
10112
|
+
}
|
|
10113
|
+
|
|
10114
|
+
if (!data.is_likely_active) {
|
|
10115
|
+
recommendations.push('Email may not be actively monitored');
|
|
10116
|
+
}
|
|
10117
|
+
|
|
10118
|
+
return recommendations;
|
|
10119
|
+
}
|
|
10120
|
+
|
|
10121
|
+
function generateVerificationSuggestions(data, emailContext) {
|
|
10122
|
+
const suggestions = [];
|
|
10123
|
+
|
|
10124
|
+
if (data.account_type === 'business' && !data.business_company_name) {
|
|
10125
|
+
suggestions.push('Verify company name through LinkedIn or company website');
|
|
10126
|
+
}
|
|
10127
|
+
|
|
10128
|
+
if (!data.is_real_person && data.account_type === 'business') {
|
|
10129
|
+
suggestions.push('Check if this is a role-based email that forwards to individuals');
|
|
10130
|
+
}
|
|
10131
|
+
|
|
10132
|
+
if (data.spam_risk_level === 'medium' || data.spam_risk_level === 'high') {
|
|
10133
|
+
suggestions.push('Send verification email before important communications');
|
|
10134
|
+
}
|
|
10135
|
+
|
|
10136
|
+
if (data.domain_analysis.is_custom_domain) {
|
|
10137
|
+
suggestions.push('Check company website for email format patterns');
|
|
10138
|
+
}
|
|
10139
|
+
|
|
10140
|
+
return suggestions;
|
|
10141
|
+
}
|
|
10142
|
+
|
|
10143
|
+
function getDefaultEmailAnalysis(name, email, emailContext = '', error = null) {
|
|
10144
|
+
const domain = email.split('@')[1] || '';
|
|
10145
|
+
const localPart = email.split('@')[0] || '';
|
|
10146
|
+
const nameParts = name.toLowerCase().split(/\s+/);
|
|
10147
|
+
const firstName = nameParts[0] || '';
|
|
10148
|
+
const lastName = nameParts.slice(-1)[0] || '';
|
|
10149
|
+
|
|
10150
|
+
const isPublicProvider = isPublicEmailProvider(domain);
|
|
10151
|
+
const accountType = isPublicProvider ? 'personal' : 'business';
|
|
10152
|
+
const isRealPerson = detectRealPerson(localPart, name, domain);
|
|
10153
|
+
|
|
10154
|
+
return {
|
|
10155
|
+
success: false,
|
|
10156
|
+
account_type: accountType,
|
|
10157
|
+
account_type_confidence: 70,
|
|
10158
|
+
|
|
10159
|
+
personal_account_provider: isPublicProvider ? identifyPersonalProvider(domain) : undefined,
|
|
10160
|
+
personal_account_type: 'unknown',
|
|
10161
|
+
personal_account_age_indicator: 'unknown',
|
|
10162
|
+
|
|
10163
|
+
business_company_name: !isPublicProvider ? inferCompanyFromDomain(domain) : undefined,
|
|
10164
|
+
business_company_domain: !isPublicProvider ? domain : undefined,
|
|
10165
|
+
business_account_category: !isPublicProvider ? (isRealPerson ? 'personal_employee' : 'generic_role') : undefined,
|
|
10166
|
+
business_account_role: undefined,
|
|
10167
|
+
business_department: undefined,
|
|
10168
|
+
|
|
10169
|
+
is_real_person: isRealPerson,
|
|
10170
|
+
person_name_in_email: checkNameInEmail(localPart, firstName, lastName),
|
|
10171
|
+
person_title_inferred: undefined,
|
|
10172
|
+
|
|
10173
|
+
email_pattern: analyzeEmailPattern(localPart, domain),
|
|
10174
|
+
|
|
10175
|
+
context_supports_business: undefined,
|
|
10176
|
+
context_supports_personal: undefined,
|
|
10177
|
+
context_company_mentions: [],
|
|
10178
|
+
|
|
10179
|
+
is_high_quality_contact: isRealPerson && !isPublicProvider,
|
|
10180
|
+
is_likely_active: true,
|
|
10181
|
+
spam_risk_level: assessSpamRisk(localPart, domain, accountType),
|
|
10182
|
+
|
|
10183
|
+
suggested_use_case: isRealPerson && !isPublicProvider ? 'sales_outreach' : 'networking',
|
|
10184
|
+
|
|
10185
|
+
analysis_timestamp: new Date().toISOString(),
|
|
10186
|
+
analysis_notes: ['Default analysis due to API failure'],
|
|
10187
|
+
|
|
10188
|
+
// Enhanced fields
|
|
10189
|
+
domain_analysis: {
|
|
10190
|
+
domain: domain,
|
|
10191
|
+
is_public_provider: isPublicProvider,
|
|
10192
|
+
is_custom_domain: isCustomDomain(domain),
|
|
10193
|
+
is_free_email: isFreeEmailDomain(domain),
|
|
10194
|
+
is_education: isEducationDomain(domain),
|
|
10195
|
+
is_government: isGovernmentDomain(domain),
|
|
10196
|
+
tld: domain.split('.').pop() || '',
|
|
10197
|
+
},
|
|
10198
|
+
|
|
10199
|
+
name_email_correlation: calculateNameEmailCorrelation(localPart, firstName, lastName),
|
|
10200
|
+
professional_score: calculateProfessionalScore({
|
|
10201
|
+
account_type: accountType,
|
|
10202
|
+
is_real_person: isRealPerson,
|
|
10203
|
+
is_likely_active: true,
|
|
10204
|
+
spam_risk_level: assessSpamRisk(localPart, domain, accountType),
|
|
10205
|
+
name_email_correlation: calculateNameEmailCorrelation(localPart, firstName, lastName),
|
|
10206
|
+
business_account_category: !isPublicProvider ? (isRealPerson ? 'personal_employee' : 'generic_role') : undefined,
|
|
10207
|
+
}),
|
|
10208
|
+
|
|
10209
|
+
recommendations: generateRecommendations({
|
|
10210
|
+
account_type: accountType,
|
|
10211
|
+
is_real_person: isRealPerson,
|
|
10212
|
+
business_account_category: !isPublicProvider ? (isRealPerson ? 'personal_employee' : 'generic_role') : undefined,
|
|
10213
|
+
spam_risk_level: assessSpamRisk(localPart, domain, accountType),
|
|
10214
|
+
is_likely_active: true,
|
|
10215
|
+
}),
|
|
10216
|
+
|
|
10217
|
+
verification_suggestions: generateVerificationSuggestions(
|
|
10218
|
+
{
|
|
10219
|
+
account_type: accountType,
|
|
10220
|
+
business_company_name: !isPublicProvider ? inferCompanyFromDomain(domain) : undefined,
|
|
10221
|
+
is_real_person: isRealPerson,
|
|
10222
|
+
spam_risk_level: assessSpamRisk(localPart, domain, accountType),
|
|
10223
|
+
domain_analysis: { is_custom_domain: isCustomDomain(domain) },
|
|
10224
|
+
},
|
|
10225
|
+
emailContext,
|
|
10226
|
+
),
|
|
10227
|
+
|
|
10228
|
+
error: error || 'API call failed',
|
|
10229
|
+
};
|
|
10230
|
+
}
|
|
10231
|
+
|
|
10232
|
+
const analysis_ret = await submit_chat_gpt_prompt({
|
|
10233
|
+
uid,
|
|
10234
|
+
prompt: `Analyze this email account: "${email}" for person: "${name}"
|
|
10235
|
+
|
|
10236
|
+
EMAIL CONTEXT (if provided):
|
|
10237
|
+
"${email_context.substring(0, 400)}${email_context.length > 400 ? '...' : ''}"
|
|
10238
|
+
|
|
10239
|
+
ANALYSIS TASKS:
|
|
10240
|
+
1. Determine if this is a PERSONAL or BUSINESS email account
|
|
10241
|
+
2. For BUSINESS accounts:
|
|
10242
|
+
- Identify the company/organization
|
|
10243
|
+
- Determine if this email belongs to a REAL PERSON at the company
|
|
10244
|
+
- Or if it's a generic/role-based account
|
|
10245
|
+
3. For PERSONAL accounts:
|
|
10246
|
+
- Identify which personal email provider is used
|
|
10247
|
+
- Determine if it's likely a primary or secondary personal account
|
|
10248
|
+
|
|
10249
|
+
ANALYSIS CRITERIA:
|
|
10250
|
+
BUSINESS EMAIL INDICATORS:
|
|
10251
|
+
- Domain matches a known company (not public email providers)
|
|
10252
|
+
- Email format matches company naming conventions
|
|
10253
|
+
- Email is mentioned in professional context
|
|
10254
|
+
- Contains company-specific signature or details
|
|
10255
|
+
|
|
10256
|
+
PERSONAL EMAIL INDICATORS:
|
|
10257
|
+
- Domain is from public email providers (gmail.com, outlook.com, yahoo.com, etc.)
|
|
10258
|
+
- Email format is personal/creative
|
|
10259
|
+
- No company affiliation in context
|
|
10260
|
+
- Used for personal communications
|
|
10261
|
+
|
|
10262
|
+
PERSON VS GENERIC BUSINESS ACCOUNT:
|
|
10263
|
+
- PERSON: Contains personal name elements (john.doe@company.com)
|
|
10264
|
+
- GENERIC: info@, support@, sales@, contact@, hello@, noreply@, etc.
|
|
10265
|
+
- DEPARTMENT: hr@, finance@, marketing@, it@, etc.
|
|
10266
|
+
|
|
10267
|
+
COMPANY DETECTION:
|
|
10268
|
+
- Extract company from email domain when possible
|
|
10269
|
+
- Use name patterns to identify likely companies
|
|
10270
|
+
- Consider email context for company references
|
|
10271
|
+
|
|
10272
|
+
PROVIDE DETAILED ANALYSIS WITH CONFIDENCE LEVELS.`,
|
|
10273
|
+
model: 'gpt-4o-minai',
|
|
10274
|
+
response_format: z.object({
|
|
10275
|
+
// Primary Classification
|
|
10276
|
+
account_type: z.enum(['personal', 'business', 'ambiguous']),
|
|
10277
|
+
account_type_confidence: z.number().min(0).max(100),
|
|
10278
|
+
|
|
10279
|
+
// Personal Account Details
|
|
10280
|
+
personal_account_provider: z.string().optional().describe('Email provider for personal accounts'),
|
|
10281
|
+
personal_account_type: z.enum(['primary', 'secondary', 'disposable', 'unknown']).optional(),
|
|
10282
|
+
personal_account_age_indicator: z.enum(['new', 'established', 'old', 'unknown']).optional(),
|
|
10283
|
+
|
|
10284
|
+
// Business Account Details
|
|
10285
|
+
business_company_name: z.string().optional().describe('Company name if business account'),
|
|
10286
|
+
business_company_domain: z.string().optional().describe('Company domain'),
|
|
10287
|
+
business_account_category: z.enum(['personal_employee', 'generic_role', 'department', 'catch_all', 'unknown']).optional(),
|
|
10288
|
+
business_account_role: z.string().optional().describe('Role suggested by email address'),
|
|
10289
|
+
business_department: z.string().optional().describe('Department if applicable'),
|
|
10290
|
+
|
|
10291
|
+
// Person Analysis
|
|
10292
|
+
is_real_person: z.boolean().describe('Whether email belongs to a real person'),
|
|
10293
|
+
person_name_in_email: z.boolean().describe("Whether person's name appears in email address"),
|
|
10294
|
+
person_title_inferred: z.string().optional().describe('Inferred job title from email pattern'),
|
|
10295
|
+
|
|
10296
|
+
// Email Pattern Analysis
|
|
10297
|
+
email_pattern: z.enum([
|
|
10298
|
+
'first.last@company.com',
|
|
10299
|
+
'first.last@domain.com',
|
|
10300
|
+
'firstlast@company.com',
|
|
10301
|
+
'flast@company.com',
|
|
10302
|
+
'first@company.com',
|
|
10303
|
+
'f.last@company.com',
|
|
10304
|
+
'initial.last@company.com',
|
|
10305
|
+
'role@company.com',
|
|
10306
|
+
'department@company.com',
|
|
10307
|
+
'info@company.com',
|
|
10308
|
+
'generic@company.com',
|
|
10309
|
+
'personal@provider.com',
|
|
10310
|
+
'custom@personal.com',
|
|
10311
|
+
'unknown',
|
|
10312
|
+
]),
|
|
10313
|
+
|
|
10314
|
+
// Context Analysis
|
|
10315
|
+
context_supports_business: z.boolean().optional().describe('Email context suggests business use'),
|
|
10316
|
+
context_supports_personal: z.boolean().optional().describe('Email context suggests personal use'),
|
|
10317
|
+
context_company_mentions: z.array(z.string()).optional().describe('Companies mentioned in context'),
|
|
10318
|
+
|
|
10319
|
+
// Risk & Quality Assessment
|
|
10320
|
+
is_high_quality_contact: z.boolean().describe('Good contact for professional outreach'),
|
|
10321
|
+
is_likely_active: z.boolean().describe('Email likely actively monitored'),
|
|
10322
|
+
spam_risk_level: z.enum(['low', 'medium', 'high', 'unknown']),
|
|
10323
|
+
|
|
10324
|
+
// Recommendations
|
|
10325
|
+
suggested_use_case: z.enum(['sales_outreach', 'recruitment', 'customer_support', 'partnership', 'networking', 'personal_contact', 'avoid']),
|
|
10326
|
+
|
|
10327
|
+
// Metadata
|
|
10328
|
+
analysis_timestamp: z.string().describe('When analysis was performed'),
|
|
10329
|
+
analysis_notes: z.array(z.string()).optional().describe('Key observations from analysis'),
|
|
10330
|
+
}),
|
|
10331
|
+
metadata: {
|
|
10332
|
+
func: 'analyze_email_account_type',
|
|
10333
|
+
person_name: name,
|
|
10334
|
+
person_email: email,
|
|
10335
|
+
has_email_context: !!email_context,
|
|
10336
|
+
email_length: email_context?.length || 0,
|
|
10337
|
+
},
|
|
10338
|
+
account_profile_info,
|
|
10339
|
+
tools: [{ type: 'web_search_preview' }],
|
|
10340
|
+
});
|
|
10341
|
+
|
|
10342
|
+
try {
|
|
10343
|
+
if (analysis_ret.code > -1) {
|
|
10344
|
+
const data = typeof analysis_ret.data === 'string' ? JSON.parse(analysis_ret.data) : analysis_ret.data;
|
|
10345
|
+
|
|
10346
|
+
// Enhance with additional analysis
|
|
10347
|
+
const enhancedData = enhanceEmailAnalysis(data, name, email, email_context);
|
|
10348
|
+
|
|
10349
|
+
return {
|
|
10350
|
+
success: true,
|
|
10351
|
+
...enhancedData,
|
|
10352
|
+
};
|
|
10353
|
+
} else {
|
|
10354
|
+
console.error('Email analysis API call failed:', analysis_ret);
|
|
10355
|
+
return getDefaultEmailAnalysis(name, email, email_context);
|
|
10356
|
+
}
|
|
10357
|
+
} catch (error) {
|
|
10358
|
+
console.error('Error in analyze_email_account_type:', error);
|
|
10359
|
+
return getDefaultEmailAnalysis(name, email, email_context, error.message);
|
|
10360
|
+
}
|
|
10361
|
+
};
|