@xuda.io/ai_module 1.1.4681 → 1.1.4683
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 +815 -24
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -678,7 +678,7 @@ const validate_table_data_doc = tool({
|
|
|
678
678
|
udfData: z.object({
|
|
679
679
|
udffileid: z.string(),
|
|
680
680
|
data: z.record(z.union([z.string(), z.number()])), // Any key-value pairs
|
|
681
|
-
error: z.object({}).optional(),
|
|
681
|
+
error: z.object({}).optional().nullable(),
|
|
682
682
|
}),
|
|
683
683
|
ts: z.number(),
|
|
684
684
|
date: z.number(),
|
|
@@ -690,7 +690,8 @@ const validate_table_data_doc = tool({
|
|
|
690
690
|
keySegments: z.array(z.any()),
|
|
691
691
|
keyValue: z.array(z.any()),
|
|
692
692
|
})
|
|
693
|
-
.optional()
|
|
693
|
+
.optional()
|
|
694
|
+
.nullable(),
|
|
694
695
|
),
|
|
695
696
|
});
|
|
696
697
|
const doc_obj = JSON5.parse(doc);
|
|
@@ -7782,8 +7783,8 @@ Category:`;
|
|
|
7782
7783
|
prompt_category: z.enum(ai_prompt_categories),
|
|
7783
7784
|
confidence_score: z.number().min(0).max(100),
|
|
7784
7785
|
primary_intent: z.string().max(100),
|
|
7785
|
-
secondary_category: z.string().optional(),
|
|
7786
|
-
complexity_level: z.enum(['Basic', 'Intermediate', 'Advanced', 'Expert']).optional(),
|
|
7786
|
+
secondary_category: z.string().optional().nullable(),
|
|
7787
|
+
complexity_level: z.enum(['Basic', 'Intermediate', 'Advanced', 'Expert']).optional().nullable(),
|
|
7787
7788
|
}),
|
|
7788
7789
|
metadata: {
|
|
7789
7790
|
func: 'categorize_ai_prompt',
|
|
@@ -9643,8 +9644,8 @@ export const get_person_info = async function (uid, name, email, account_profile
|
|
|
9643
9644
|
person_full_name: z.string().describe("Person's full name"),
|
|
9644
9645
|
person_first_name: z.string().describe('First name'),
|
|
9645
9646
|
person_last_name: z.string().describe('Last name'),
|
|
9646
|
-
person_middle_name: z.string().optional().describe('Middle name if available'),
|
|
9647
|
-
person_preferred_name: z.string().optional().describe('Preferred/nickname if different'),
|
|
9647
|
+
person_middle_name: z.string().optional().nullable().describe('Middle name if available'),
|
|
9648
|
+
person_preferred_name: z.string().optional().nullable().describe('Preferred/nickname if different'),
|
|
9648
9649
|
|
|
9649
9650
|
// Demographic Information
|
|
9650
9651
|
person_age: z.union([z.number().int().min(18).max(100), z.string().describe("Age range or 'unknown'")]).describe('Estimated age or range'),
|
|
@@ -9666,7 +9667,7 @@ export const get_person_info = async function (uid, name, email, account_profile
|
|
|
9666
9667
|
// Contact Information
|
|
9667
9668
|
person_phone: z.string().describe("Phone number or 'Not available'"),
|
|
9668
9669
|
person_email: z.string().describe('Primary email address'),
|
|
9669
|
-
person_email_alternate: z.string().optional().describe('Alternate email if available'),
|
|
9670
|
+
person_email_alternate: z.string().optional().nullable().describe('Alternate email if available'),
|
|
9670
9671
|
|
|
9671
9672
|
// Education
|
|
9672
9673
|
person_education: z.array(z.string()).describe('Array of educational institutions/degrees'),
|
|
@@ -9683,16 +9684,16 @@ export const get_person_info = async function (uid, name, email, account_profile
|
|
|
9683
9684
|
person_twitter: z.string().describe("Twitter/X profile URL or 'Not available'"),
|
|
9684
9685
|
person_github: z.string().describe("GitHub profile URL or 'Not available'"),
|
|
9685
9686
|
person_website: z.string().describe("Personal website/portfolio or 'Not available'"),
|
|
9686
|
-
person_social_other: z.array(z.string()).optional().describe('Other social media profiles'),
|
|
9687
|
+
person_social_other: z.array(z.string()).optional().nullable().describe('Other social media profiles'),
|
|
9687
9688
|
|
|
9688
9689
|
// Additional Information
|
|
9689
9690
|
person_languages: z.array(z.string()).describe('Languages spoken'),
|
|
9690
9691
|
person_interests: z.array(z.string()).describe('Professional/personal interests'),
|
|
9691
|
-
person_achievements: z.array(z.string()).optional().describe('Notable achievements/awards'),
|
|
9692
|
-
person_current_projects: z.array(z.string()).optional().describe('Current projects'),
|
|
9692
|
+
person_achievements: z.array(z.string()).optional().nullable().describe('Notable achievements/awards'),
|
|
9693
|
+
person_current_projects: z.array(z.string()).optional().nullable().describe('Current projects'),
|
|
9693
9694
|
|
|
9694
9695
|
// Context Extracted Information
|
|
9695
|
-
person_context_insights: z.array(z.string()).optional().describe('Insights extracted from provided email context'),
|
|
9696
|
+
person_context_insights: z.array(z.string()).optional().nullable().describe('Insights extracted from provided email context'),
|
|
9696
9697
|
|
|
9697
9698
|
// Metadata
|
|
9698
9699
|
person_available_for_opportunities: z.boolean().describe('Open to new opportunities'),
|
|
@@ -10277,21 +10278,21 @@ PROVIDE DETAILED ANALYSIS WITH CONFIDENCE LEVELS.`,
|
|
|
10277
10278
|
account_type_confidence: z.number().min(0).max(100),
|
|
10278
10279
|
|
|
10279
10280
|
// 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(),
|
|
10281
|
+
personal_account_provider: z.string().optional().nullable().describe('Email provider for personal accounts'),
|
|
10282
|
+
personal_account_type: z.enum(['primary', 'secondary', 'disposable', 'unknown']).optional().nullable(),
|
|
10283
|
+
personal_account_age_indicator: z.enum(['new', 'established', 'old', 'unknown']).optional().nullable(),
|
|
10283
10284
|
|
|
10284
10285
|
// 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'),
|
|
10286
|
+
business_company_name: z.string().optional().nullable().describe('Company name if business account'),
|
|
10287
|
+
business_company_domain: z.string().optional().nullable().describe('Company domain'),
|
|
10288
|
+
business_account_category: z.enum(['personal_employee', 'generic_role', 'department', 'catch_all', 'unknown']).optional().nullable(),
|
|
10289
|
+
business_account_role: z.string().optional().nullable().describe('Role suggested by email address'),
|
|
10290
|
+
business_department: z.string().optional().nullable().describe('Department if applicable'),
|
|
10290
10291
|
|
|
10291
10292
|
// Person Analysis
|
|
10292
10293
|
is_real_person: z.boolean().describe('Whether email belongs to a real person'),
|
|
10293
10294
|
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
|
+
person_title_inferred: z.string().optional().nullable().describe('Inferred job title from email pattern'),
|
|
10295
10296
|
|
|
10296
10297
|
// Email Pattern Analysis
|
|
10297
10298
|
email_pattern: z.enum([
|
|
@@ -10312,9 +10313,9 @@ PROVIDE DETAILED ANALYSIS WITH CONFIDENCE LEVELS.`,
|
|
|
10312
10313
|
]),
|
|
10313
10314
|
|
|
10314
10315
|
// 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'),
|
|
10316
|
+
context_supports_business: z.boolean().optional().nullable().describe('Email context suggests business use'),
|
|
10317
|
+
context_supports_personal: z.boolean().optional().nullable().describe('Email context suggests personal use'),
|
|
10318
|
+
context_company_mentions: z.array(z.string()).optional().nullable().describe('Companies mentioned in context'),
|
|
10318
10319
|
|
|
10319
10320
|
// Risk & Quality Assessment
|
|
10320
10321
|
is_high_quality_contact: z.boolean().describe('Good contact for professional outreach'),
|
|
@@ -10326,7 +10327,7 @@ PROVIDE DETAILED ANALYSIS WITH CONFIDENCE LEVELS.`,
|
|
|
10326
10327
|
|
|
10327
10328
|
// Metadata
|
|
10328
10329
|
analysis_timestamp: z.string().describe('When analysis was performed'),
|
|
10329
|
-
analysis_notes: z.array(z.string()).optional().describe('Key observations from analysis'),
|
|
10330
|
+
analysis_notes: z.array(z.string()).optional().nullable().describe('Key observations from analysis'),
|
|
10330
10331
|
}),
|
|
10331
10332
|
metadata: {
|
|
10332
10333
|
func: 'analyze_email_account_type',
|
|
@@ -10359,3 +10360,793 @@ PROVIDE DETAILED ANALYSIS WITH CONFIDENCE LEVELS.`,
|
|
|
10359
10360
|
return getDefaultEmailAnalysis(name, email, email_context, error.message);
|
|
10360
10361
|
}
|
|
10361
10362
|
};
|
|
10363
|
+
|
|
10364
|
+
export const detect_email_type = async function (uid, name, email, account_profile_info, email_body = '', email_subject = '') {
|
|
10365
|
+
// ========== HELPER FUNCTIONS ==========
|
|
10366
|
+
|
|
10367
|
+
async function hashString(str) {
|
|
10368
|
+
const encoder = new TextEncoder();
|
|
10369
|
+
const data = encoder.encode(str);
|
|
10370
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
10371
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
10372
|
+
return hashArray
|
|
10373
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
10374
|
+
.join('')
|
|
10375
|
+
.substring(0, 16);
|
|
10376
|
+
}
|
|
10377
|
+
|
|
10378
|
+
function enhanceEmailDetection(data, name, email, emailBody, emailSubject) {
|
|
10379
|
+
// Run additional local analysis
|
|
10380
|
+
const localAnalysis = analyzeEmailLocally(emailBody, emailSubject, email, name);
|
|
10381
|
+
|
|
10382
|
+
// Combine AI analysis with local analysis
|
|
10383
|
+
const combinedData = {
|
|
10384
|
+
...data,
|
|
10385
|
+
// Override with local analysis if confidence is high
|
|
10386
|
+
spam_confidence: Math.max(data.spam_confidence, localAnalysis.spamScore),
|
|
10387
|
+
phishing_risk: localAnalysis.phishingScore > 70 ? 'high' : data.phishing_risk,
|
|
10388
|
+
|
|
10389
|
+
// Add local analysis results
|
|
10390
|
+
local_analysis: localAnalysis,
|
|
10391
|
+
|
|
10392
|
+
// Enhance sender analysis
|
|
10393
|
+
sender_analysis: analyzeSender(email, name, emailBody),
|
|
10394
|
+
|
|
10395
|
+
// Content statistics
|
|
10396
|
+
content_stats: {
|
|
10397
|
+
word_count: (emailBody.match(/\S+/g) || []).length,
|
|
10398
|
+
sentence_count: (emailBody.match(/[.!?]+/g) || []).length,
|
|
10399
|
+
link_count: (emailBody.match(/https?:\/\/[^\s]+/g) || []).length,
|
|
10400
|
+
uppercase_ratio: calculateUppercaseRatio(emailBody),
|
|
10401
|
+
exclamation_count: (emailBody.match(/!/g) || []).length,
|
|
10402
|
+
dollar_sign_count: (emailBody.match(/\$/g) || []).length,
|
|
10403
|
+
phone_patterns: (emailBody.match(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g) || []).length,
|
|
10404
|
+
},
|
|
10405
|
+
|
|
10406
|
+
// Behavioral patterns
|
|
10407
|
+
behavioral_patterns: detectBehavioralPatterns(emailBody, emailSubject),
|
|
10408
|
+
|
|
10409
|
+
// Domain reputation
|
|
10410
|
+
domain_reputation: assessDomainReputation(email),
|
|
10411
|
+
|
|
10412
|
+
// Add comprehensive risk assessment
|
|
10413
|
+
comprehensive_risk_score: calculateComprehensiveRisk(data, localAnalysis),
|
|
10414
|
+
|
|
10415
|
+
// Filter rules that matched
|
|
10416
|
+
matched_filters: detectMatchedFilters(emailBody, emailSubject, email, name),
|
|
10417
|
+
|
|
10418
|
+
// Timeline analysis
|
|
10419
|
+
temporal_analysis: {
|
|
10420
|
+
is_time_sensitive: detectTimeSensitivity(emailBody),
|
|
10421
|
+
has_expiry: detectExpiryDate(emailBody),
|
|
10422
|
+
is_follow_up: detectFollowUpPattern(emailBody, emailSubject),
|
|
10423
|
+
},
|
|
10424
|
+
|
|
10425
|
+
// Relationship context
|
|
10426
|
+
relationship_context: analyzeRelationshipContext(emailBody, emailSubject, name),
|
|
10427
|
+
|
|
10428
|
+
// Update timestamp
|
|
10429
|
+
analysis_timestamp: new Date().toISOString(),
|
|
10430
|
+
content_length: emailBody.length,
|
|
10431
|
+
};
|
|
10432
|
+
|
|
10433
|
+
// Calculate final classification confidence
|
|
10434
|
+
combinedData.final_confidence = calculateFinalConfidence(combinedData);
|
|
10435
|
+
|
|
10436
|
+
// Generate detailed explanation
|
|
10437
|
+
combinedData.explanation = generateExplanation(combinedData);
|
|
10438
|
+
|
|
10439
|
+
// Add compliance flags
|
|
10440
|
+
combinedData.compliance_flags = checkCompliance(emailBody, emailSubject);
|
|
10441
|
+
|
|
10442
|
+
return combinedData;
|
|
10443
|
+
}
|
|
10444
|
+
|
|
10445
|
+
function analyzeEmailLocally(body, subject, email, name) {
|
|
10446
|
+
const text = (subject + ' ' + body).toLowerCase();
|
|
10447
|
+
let spamScore = 0;
|
|
10448
|
+
let phishingScore = 0;
|
|
10449
|
+
let promotionalScore = 0;
|
|
10450
|
+
let transactionalScore = 0;
|
|
10451
|
+
|
|
10452
|
+
// SPAM indicators
|
|
10453
|
+
const spamKeywords = [
|
|
10454
|
+
'congratulations',
|
|
10455
|
+
'winner',
|
|
10456
|
+
'prize',
|
|
10457
|
+
'lottery',
|
|
10458
|
+
'free',
|
|
10459
|
+
'guaranteed',
|
|
10460
|
+
'risk-free',
|
|
10461
|
+
'act now',
|
|
10462
|
+
'limited time',
|
|
10463
|
+
'urgent',
|
|
10464
|
+
'important',
|
|
10465
|
+
'attention',
|
|
10466
|
+
'alert',
|
|
10467
|
+
'click here',
|
|
10468
|
+
'buy now',
|
|
10469
|
+
'order now',
|
|
10470
|
+
'discount',
|
|
10471
|
+
'save big',
|
|
10472
|
+
'cheap',
|
|
10473
|
+
'viagra',
|
|
10474
|
+
'cialis',
|
|
10475
|
+
'pharmacy',
|
|
10476
|
+
'prescription',
|
|
10477
|
+
'enlarge',
|
|
10478
|
+
'weight loss',
|
|
10479
|
+
'nigerian',
|
|
10480
|
+
'prince',
|
|
10481
|
+
'inheritance',
|
|
10482
|
+
'unclaimed',
|
|
10483
|
+
'bank account',
|
|
10484
|
+
'password',
|
|
10485
|
+
'account suspended',
|
|
10486
|
+
'verify',
|
|
10487
|
+
'security alert',
|
|
10488
|
+
'dear friend',
|
|
10489
|
+
'dear customer',
|
|
10490
|
+
'dear account holder',
|
|
10491
|
+
];
|
|
10492
|
+
|
|
10493
|
+
spamKeywords.forEach((keyword) => {
|
|
10494
|
+
if (text.includes(keyword)) spamScore += 2;
|
|
10495
|
+
});
|
|
10496
|
+
|
|
10497
|
+
// Phishing indicators
|
|
10498
|
+
const phishingPatterns = [/login\s*(?:here|now)/i, /verify\s*(?:your|my)\s*account/i, /password\s*(?:reset|expired|change)/i, /suspended\s*account/i, /unauthorized\s*activity/i, /security\s*breach/i, /update\s*(?:your|my)\s*information/i, /confirm\s*(?:your|my)\s*identity/i];
|
|
10499
|
+
|
|
10500
|
+
phishingPatterns.forEach((pattern) => {
|
|
10501
|
+
if (pattern.test(text)) phishingScore += 15;
|
|
10502
|
+
});
|
|
10503
|
+
|
|
10504
|
+
// Promotional indicators
|
|
10505
|
+
const promotionalKeywords = ['newsletter', 'subscribe', 'unsubscribe', 'marketing', 'promotion', 'sale', 'offer', 'deal', 'coupon', 'voucher', 'discount code', 'new product', 'announcement', 'launch', 'event', 'webinar', 'limited offer', 'exclusive', 'special offer', 'members only'];
|
|
10506
|
+
|
|
10507
|
+
promotionalKeywords.forEach((keyword) => {
|
|
10508
|
+
if (text.includes(keyword)) promotionalScore += 3;
|
|
10509
|
+
});
|
|
10510
|
+
|
|
10511
|
+
// Transactional indicators
|
|
10512
|
+
const transactionalKeywords = ['order', 'invoice', 'receipt', 'payment', 'booking', 'reservation', 'confirmation', 'shipping', 'delivery', 'tracking', 'shipment', 'appointment', 'meeting', 'reminder', 'bill', 'statement', 'account', 'statement', 'balance', 'transaction'];
|
|
10513
|
+
|
|
10514
|
+
transactionalKeywords.forEach((keyword) => {
|
|
10515
|
+
if (text.includes(keyword)) transactionalScore += 3;
|
|
10516
|
+
});
|
|
10517
|
+
|
|
10518
|
+
// Link analysis
|
|
10519
|
+
const links = body.match(/https?:\/\/[^\s]+/g) || [];
|
|
10520
|
+
const suspiciousDomains = ['bit.ly', 'tinyurl', 'shorte.st', 'adf.ly', 'goo.gl'];
|
|
10521
|
+
|
|
10522
|
+
links.forEach((link) => {
|
|
10523
|
+
if (suspiciousDomains.some((domain) => link.includes(domain))) {
|
|
10524
|
+
spamScore += 10;
|
|
10525
|
+
phishingScore += 10;
|
|
10526
|
+
}
|
|
10527
|
+
});
|
|
10528
|
+
|
|
10529
|
+
// Grammar/spelling analysis (simple)
|
|
10530
|
+
const misspellings = body.match(/\b(?:recieve|seperate|definately|occured|tomm?orrow)\b/gi) || [];
|
|
10531
|
+
spamScore += misspellings.length * 2;
|
|
10532
|
+
|
|
10533
|
+
// Urgency analysis
|
|
10534
|
+
const urgencyWords = ['urgent', 'immediately', 'asap', 'right now', 'today only'];
|
|
10535
|
+
let urgencyCount = 0;
|
|
10536
|
+
urgencyWords.forEach((word) => {
|
|
10537
|
+
if (text.includes(word)) urgencyCount++;
|
|
10538
|
+
});
|
|
10539
|
+
spamScore += urgencyCount * 5;
|
|
10540
|
+
|
|
10541
|
+
// Personalization check
|
|
10542
|
+
const hasName = name && body.toLowerCase().includes(name.toLowerCase().split(' ')[0]);
|
|
10543
|
+
const personalPronouns = ['you', 'your', 'yours'];
|
|
10544
|
+
let personalCount = 0;
|
|
10545
|
+
personalPronouns.forEach((pronoun) => {
|
|
10546
|
+
const regex = new RegExp(`\\b${pronoun}\\b`, 'gi');
|
|
10547
|
+
personalCount += (body.match(regex) || []).length;
|
|
10548
|
+
});
|
|
10549
|
+
|
|
10550
|
+
const personalizationScore = hasName ? 30 : Math.min(personalCount * 2, 20);
|
|
10551
|
+
|
|
10552
|
+
return {
|
|
10553
|
+
spamScore: Math.min(spamScore, 100),
|
|
10554
|
+
phishingScore: Math.min(phishingScore, 100),
|
|
10555
|
+
promotionalScore: Math.min(promotionalScore, 100),
|
|
10556
|
+
transactionalScore: Math.min(transactionalScore, 100),
|
|
10557
|
+
personalizationScore,
|
|
10558
|
+
linkCount: links.length,
|
|
10559
|
+
hasUnsubscribe: text.includes('unsubscribe') || text.includes('opt-out'),
|
|
10560
|
+
hasPhone: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/.test(body),
|
|
10561
|
+
hasAddress: /\b\d+\s+[\w\s]+(?:street|st|avenue|ave|road|rd|boulevard|blvd|way)\b/i.test(body),
|
|
10562
|
+
urgencyCount,
|
|
10563
|
+
misspellingCount: misspellings.length,
|
|
10564
|
+
};
|
|
10565
|
+
}
|
|
10566
|
+
|
|
10567
|
+
function analyzeSender(email, name, body) {
|
|
10568
|
+
const domain = email.split('@')[1] || '';
|
|
10569
|
+
const localPart = email.split('@')[0] || '';
|
|
10570
|
+
|
|
10571
|
+
return {
|
|
10572
|
+
email: email,
|
|
10573
|
+
domain: domain,
|
|
10574
|
+
local_part: localPart,
|
|
10575
|
+
is_public_domain: isPublicEmailDomain(domain),
|
|
10576
|
+
is_generic_sender: isGenericSender(localPart, name),
|
|
10577
|
+
domain_age_indicator: assessDomainAge(domain),
|
|
10578
|
+
sender_consistency: checkSenderConsistency(name, email, body),
|
|
10579
|
+
};
|
|
10580
|
+
}
|
|
10581
|
+
|
|
10582
|
+
function isPublicEmailDomain(domain) {
|
|
10583
|
+
const publicDomains = ['gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com', 'aol.com', 'icloud.com', 'protonmail.com', 'zoho.com', 'yandex.com'];
|
|
10584
|
+
return publicDomains.includes(domain.toLowerCase());
|
|
10585
|
+
}
|
|
10586
|
+
|
|
10587
|
+
function isGenericSender(localPart, name) {
|
|
10588
|
+
const genericSenders = ['info', 'support', 'sales', 'contact', 'hello', 'noreply', 'admin', 'newsletter', 'marketing', 'team', 'notifications'];
|
|
10589
|
+
|
|
10590
|
+
const localLower = localPart.toLowerCase();
|
|
10591
|
+
const nameLower = name.toLowerCase();
|
|
10592
|
+
|
|
10593
|
+
return genericSenders.some((sender) => localLower.includes(sender) || nameLower.includes(sender));
|
|
10594
|
+
}
|
|
10595
|
+
|
|
10596
|
+
function assessDomainAge(domain) {
|
|
10597
|
+
// This is a simplified version - in production, you'd use a domain age API
|
|
10598
|
+
const newDomains = ['xyz', 'online', 'site', 'top', 'club', 'shop'];
|
|
10599
|
+
const tld = domain.split('.').pop() || '';
|
|
10600
|
+
|
|
10601
|
+
if (newDomains.includes(tld)) return 'likely_new';
|
|
10602
|
+
if (['com', 'org', 'net', 'edu', 'gov'].includes(tld)) return 'likely_established';
|
|
10603
|
+
return 'unknown';
|
|
10604
|
+
}
|
|
10605
|
+
|
|
10606
|
+
function checkSenderConsistency(name, email, body) {
|
|
10607
|
+
const emailName = email.split('@')[0].toLowerCase();
|
|
10608
|
+
const bodyName = name.toLowerCase();
|
|
10609
|
+
|
|
10610
|
+
// Check if name appears in body
|
|
10611
|
+
const nameInBody = body.toLowerCase().includes(bodyName.split(' ')[0]);
|
|
10612
|
+
|
|
10613
|
+
// Check email-name consistency patterns
|
|
10614
|
+
const nameParts = bodyName.split(' ');
|
|
10615
|
+
const firstName = nameParts[0] || '';
|
|
10616
|
+
const lastName = nameParts.slice(-1)[0] || '';
|
|
10617
|
+
|
|
10618
|
+
const patterns = [`${firstName}.${lastName}`, `${firstName}${lastName}`, `${firstName.charAt(0)}${lastName}`, `${firstName}`];
|
|
10619
|
+
|
|
10620
|
+
const consistent = patterns.some((pattern) => emailName.includes(pattern.toLowerCase()));
|
|
10621
|
+
|
|
10622
|
+
return {
|
|
10623
|
+
name_in_body: nameInBody,
|
|
10624
|
+
email_name_consistent: consistent,
|
|
10625
|
+
consistency_score: (nameInBody ? 40 : 0) + (consistent ? 60 : 0),
|
|
10626
|
+
};
|
|
10627
|
+
}
|
|
10628
|
+
|
|
10629
|
+
function calculateUppercaseRatio(text) {
|
|
10630
|
+
const letters = text.replace(/[^a-zA-Z]/g, '');
|
|
10631
|
+
if (letters.length === 0) return 0;
|
|
10632
|
+
|
|
10633
|
+
const uppercase = letters.replace(/[^A-Z]/g, '');
|
|
10634
|
+
return (uppercase.length / letters.length) * 100;
|
|
10635
|
+
}
|
|
10636
|
+
|
|
10637
|
+
function detectBehavioralPatterns(body, subject) {
|
|
10638
|
+
const text = (subject + ' ' + body).toLowerCase();
|
|
10639
|
+
const patterns = [];
|
|
10640
|
+
|
|
10641
|
+
if (/(?:click|tap)\s+(?:here|this|link|button)/i.test(text)) {
|
|
10642
|
+
patterns.push('call_to_action_link');
|
|
10643
|
+
}
|
|
10644
|
+
|
|
10645
|
+
if (/limited\s+time|offer\s+expires|only\s+\d+\s+left/i.test(text)) {
|
|
10646
|
+
patterns.push('scarcity_tactic');
|
|
10647
|
+
}
|
|
10648
|
+
|
|
10649
|
+
if (/\$\d+|\d+\s*%|\d+\s*off|discount|save|sale/i.test(text)) {
|
|
10650
|
+
patterns.push('monetary_offer');
|
|
10651
|
+
}
|
|
10652
|
+
|
|
10653
|
+
if (/urgent|important|alert|attention|warning/i.test(text)) {
|
|
10654
|
+
patterns.push('urgency_tactic');
|
|
10655
|
+
}
|
|
10656
|
+
|
|
10657
|
+
if (/(?:please|kindly)\s+(?:help|assist|reply)/i.test(text)) {
|
|
10658
|
+
patterns.push('polite_request');
|
|
10659
|
+
}
|
|
10660
|
+
|
|
10661
|
+
if (/unsubscribe|opt.?out|preference|manage subscription/i.test(text)) {
|
|
10662
|
+
patterns.push('subscription_management');
|
|
10663
|
+
}
|
|
10664
|
+
|
|
10665
|
+
return patterns;
|
|
10666
|
+
}
|
|
10667
|
+
|
|
10668
|
+
function assessDomainReputation(email) {
|
|
10669
|
+
const domain = email.split('@')[1] || '';
|
|
10670
|
+
|
|
10671
|
+
// Known spam domains (simplified list)
|
|
10672
|
+
const spamDomains = ['spam4.me', 'trashmail.com', 'mailinator.com', 'guerrillamail.com', 'tempmail.com', 'yopmail.com', 'dispostable.com'];
|
|
10673
|
+
|
|
10674
|
+
if (spamDomains.includes(domain.toLowerCase())) {
|
|
10675
|
+
return 'known_spam_domain';
|
|
10676
|
+
}
|
|
10677
|
+
|
|
10678
|
+
// Professional domains
|
|
10679
|
+
const professionalTLDs = ['com', 'org', 'net', 'edu', 'gov', 'io', 'ai', 'tech'];
|
|
10680
|
+
const tld = domain.split('.').pop() || '';
|
|
10681
|
+
|
|
10682
|
+
if (professionalTLDs.includes(tld.toLowerCase())) {
|
|
10683
|
+
return 'professional_domain';
|
|
10684
|
+
}
|
|
10685
|
+
|
|
10686
|
+
// New/suspicious TLDs
|
|
10687
|
+
const suspiciousTLDs = ['xyz', 'top', 'win', 'bid', 'download', 'stream'];
|
|
10688
|
+
if (suspiciousTLDs.includes(tld.toLowerCase())) {
|
|
10689
|
+
return 'suspicious_tld';
|
|
10690
|
+
}
|
|
10691
|
+
|
|
10692
|
+
return 'neutral';
|
|
10693
|
+
}
|
|
10694
|
+
|
|
10695
|
+
function calculateComprehensiveRisk(aiData, localAnalysis) {
|
|
10696
|
+
let risk = 0;
|
|
10697
|
+
|
|
10698
|
+
// Base on AI classification
|
|
10699
|
+
switch (aiData.email_type) {
|
|
10700
|
+
case 'spam':
|
|
10701
|
+
risk += 80;
|
|
10702
|
+
break;
|
|
10703
|
+
case 'promotional':
|
|
10704
|
+
risk += 30;
|
|
10705
|
+
break;
|
|
10706
|
+
case 'transactional':
|
|
10707
|
+
risk += 10;
|
|
10708
|
+
break;
|
|
10709
|
+
case 'personal':
|
|
10710
|
+
risk += 5;
|
|
10711
|
+
break;
|
|
10712
|
+
case 'business':
|
|
10713
|
+
risk += 15;
|
|
10714
|
+
break;
|
|
10715
|
+
}
|
|
10716
|
+
|
|
10717
|
+
// Add local analysis scores
|
|
10718
|
+
risk += localAnalysis.spamScore * 0.2;
|
|
10719
|
+
risk += localAnalysis.phishingScore * 0.3;
|
|
10720
|
+
|
|
10721
|
+
// Adjust for phishing risk
|
|
10722
|
+
switch (aiData.phishing_risk) {
|
|
10723
|
+
case 'critical':
|
|
10724
|
+
risk += 40;
|
|
10725
|
+
break;
|
|
10726
|
+
case 'high':
|
|
10727
|
+
risk += 30;
|
|
10728
|
+
break;
|
|
10729
|
+
case 'medium':
|
|
10730
|
+
risk += 15;
|
|
10731
|
+
break;
|
|
10732
|
+
case 'low':
|
|
10733
|
+
risk += 5;
|
|
10734
|
+
break;
|
|
10735
|
+
}
|
|
10736
|
+
|
|
10737
|
+
// Adjust for unsolicited
|
|
10738
|
+
if (aiData.is_unsolicited) risk += 20;
|
|
10739
|
+
|
|
10740
|
+
// Adjust for urgency
|
|
10741
|
+
if (aiData.is_urgent) risk += 10;
|
|
10742
|
+
|
|
10743
|
+
return Math.min(risk, 100);
|
|
10744
|
+
}
|
|
10745
|
+
|
|
10746
|
+
function detectMatchedFilters(body, subject, email, name) {
|
|
10747
|
+
const filters = [];
|
|
10748
|
+
const text = (subject + ' ' + body).toLowerCase();
|
|
10749
|
+
|
|
10750
|
+
// Spam filters
|
|
10751
|
+
if (/(?:viagra|cialis|penis|enlarge)/i.test(text)) filters.push('adult_content_filter');
|
|
10752
|
+
if (/(?:lottery|winner|prize|jackpot)/i.test(text)) filters.push('lottery_scam_filter');
|
|
10753
|
+
if (/(?:nigerian|prince|inheritance|unclaimed)/i.test(text)) filters.push('inheritance_scam_filter');
|
|
10754
|
+
if (/password\s+reset|verify\s+account/i.test(text)) filters.push('account_verification_filter');
|
|
10755
|
+
|
|
10756
|
+
// Promotional filters
|
|
10757
|
+
if (/newsletter|subscribe|unsubscribe/i.test(text)) filters.push('newsletter_filter');
|
|
10758
|
+
if (/sale|discount|offer|coupon/i.test(text)) filters.push('promotional_offer_filter');
|
|
10759
|
+
if (/webinar|event|conference/i.test(text)) filters.push('event_filter');
|
|
10760
|
+
|
|
10761
|
+
// Transactional filters
|
|
10762
|
+
if (/order|invoice|receipt|payment/i.test(text)) filters.push('transaction_filter');
|
|
10763
|
+
if (/shipping|delivery|tracking/i.test(text)) filters.push('shipping_filter');
|
|
10764
|
+
if (/appointment|meeting|reminder/i.test(text)) filters.push('calendar_filter');
|
|
10765
|
+
|
|
10766
|
+
return filters;
|
|
10767
|
+
}
|
|
10768
|
+
|
|
10769
|
+
function detectTimeSensitivity(body) {
|
|
10770
|
+
const text = body.toLowerCase();
|
|
10771
|
+
const patterns = [/today|tomorrow|this\s+week|immediately|asap/i, /deadline|due\s+by|expires|limited\s+time/i, /urgent|important|attention|alert/i];
|
|
10772
|
+
|
|
10773
|
+
return patterns.some((pattern) => pattern.test(text));
|
|
10774
|
+
}
|
|
10775
|
+
|
|
10776
|
+
function detectExpiryDate(body) {
|
|
10777
|
+
const datePatterns = [/\b\d{1,2}\/\d{1,2}\/\d{2,4}\b/g, /\b\d{1,2}\s+(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\s+\d{2,4}\b/gi, /\bexpires?\s+(?:on|by)?\s*[:]?\s*\d{1,2}\/\d{1,2}\/\d{2,4}\b/gi];
|
|
10778
|
+
|
|
10779
|
+
return datePatterns.some((pattern) => pattern.test(body.toLowerCase()));
|
|
10780
|
+
}
|
|
10781
|
+
|
|
10782
|
+
function detectFollowUpPattern(body, subject) {
|
|
10783
|
+
const text = (subject + ' ' + body).toLowerCase();
|
|
10784
|
+
return /follow.?up|following.?up|checking.?in|touch.?base|re:.?re:/i.test(text);
|
|
10785
|
+
}
|
|
10786
|
+
|
|
10787
|
+
function analyzeRelationshipContext(body, subject, name) {
|
|
10788
|
+
const text = (subject + ' ' + body).toLowerCase();
|
|
10789
|
+
const nameParts = name.toLowerCase().split(' ');
|
|
10790
|
+
const firstName = nameParts[0] || '';
|
|
10791
|
+
|
|
10792
|
+
const indicators = {
|
|
10793
|
+
uses_name: firstName && text.includes(firstName),
|
|
10794
|
+
uses_personal_pronouns: /\b(?:i|me|my|mine|you|your|yours)\b/gi.test(text),
|
|
10795
|
+
has_greeting: /^(?:hi|hello|hey|dear|greetings)\b/im.test(body),
|
|
10796
|
+
has_signature: /(?:best|regards|sincerely|thanks|thank you)\s*[,]?\s*\n/im.test(body),
|
|
10797
|
+
has_questions: /\?/.test(text) && /(?:can|could|would|will|do|are|is)\s+you/i.test(text),
|
|
10798
|
+
};
|
|
10799
|
+
|
|
10800
|
+
const relationshipScore = Object.values(indicators).filter(Boolean).length * 20;
|
|
10801
|
+
|
|
10802
|
+
return {
|
|
10803
|
+
...indicators,
|
|
10804
|
+
relationship_score: Math.min(relationshipScore, 100),
|
|
10805
|
+
likely_known_sender: relationshipScore >= 40,
|
|
10806
|
+
};
|
|
10807
|
+
}
|
|
10808
|
+
|
|
10809
|
+
function calculateFinalConfidence(data) {
|
|
10810
|
+
let confidence = data.email_type_confidence;
|
|
10811
|
+
|
|
10812
|
+
// Adjust based on local analysis
|
|
10813
|
+
if (data.local_analysis.spamScore > 70 && data.email_type !== 'spam') {
|
|
10814
|
+
confidence -= 20;
|
|
10815
|
+
}
|
|
10816
|
+
|
|
10817
|
+
// Adjust based on phishing risk
|
|
10818
|
+
if (data.phishing_risk === 'critical' || data.phishing_risk === 'high') {
|
|
10819
|
+
confidence += 15;
|
|
10820
|
+
}
|
|
10821
|
+
|
|
10822
|
+
// Adjust based on personalization
|
|
10823
|
+
if (data.is_personalized) {
|
|
10824
|
+
confidence += 10;
|
|
10825
|
+
}
|
|
10826
|
+
|
|
10827
|
+
// Adjust based on content stats
|
|
10828
|
+
if (data.content_stats.link_count > 5) {
|
|
10829
|
+
confidence += 5;
|
|
10830
|
+
}
|
|
10831
|
+
|
|
10832
|
+
return Math.max(0, Math.min(100, confidence));
|
|
10833
|
+
}
|
|
10834
|
+
|
|
10835
|
+
function generateExplanation(data) {
|
|
10836
|
+
const explanations = [];
|
|
10837
|
+
|
|
10838
|
+
if (data.email_type === 'spam') {
|
|
10839
|
+
explanations.push('Classified as spam due to:');
|
|
10840
|
+
if (data.spam_confidence > 70) explanations.push(`- High spam confidence (${data.spam_confidence}%)`);
|
|
10841
|
+
if (data.phishing_risk !== 'none') explanations.push(`- ${data.phishing_risk} phishing risk`);
|
|
10842
|
+
if (data.local_analysis.spamScore > 60) explanations.push('- Contains spam indicators');
|
|
10843
|
+
}
|
|
10844
|
+
|
|
10845
|
+
if (data.email_type === 'promotional') {
|
|
10846
|
+
explanations.push('Classified as promotional due to:');
|
|
10847
|
+
if (data.is_commercial) explanations.push('- Commercial intent detected');
|
|
10848
|
+
if (data.local_analysis.hasUnsubscribe) explanations.push('- Contains unsubscribe option');
|
|
10849
|
+
if (data.local_analysis.promotionalScore > 30) explanations.push('- Promotional content detected');
|
|
10850
|
+
}
|
|
10851
|
+
|
|
10852
|
+
if (data.key_indicators && data.key_indicators.length > 0) {
|
|
10853
|
+
explanations.push('Key indicators:');
|
|
10854
|
+
data.key_indicators.forEach((indicator) => {
|
|
10855
|
+
explanations.push(`- ${indicator}`);
|
|
10856
|
+
});
|
|
10857
|
+
}
|
|
10858
|
+
|
|
10859
|
+
return explanations.join('\n');
|
|
10860
|
+
}
|
|
10861
|
+
|
|
10862
|
+
function checkCompliance(body, subject) {
|
|
10863
|
+
const text = (subject + ' ' + body).toLowerCase();
|
|
10864
|
+
const flags = [];
|
|
10865
|
+
|
|
10866
|
+
// CAN-SPAM Act compliance (US)
|
|
10867
|
+
if (text.includes('unsubscribe') || text.includes('opt-out')) {
|
|
10868
|
+
flags.push('has_unsubscribe_option');
|
|
10869
|
+
}
|
|
10870
|
+
|
|
10871
|
+
if (/\b\d{10}\b/.test(body.replace(/\D/g, ''))) {
|
|
10872
|
+
flags.push('contains_phone_number');
|
|
10873
|
+
}
|
|
10874
|
+
|
|
10875
|
+
if (/address\s*[:]?\s*\d+\s+[\w\s]+/i.test(body)) {
|
|
10876
|
+
flags.push('contains_physical_address');
|
|
10877
|
+
}
|
|
10878
|
+
|
|
10879
|
+
// GDPR indicators (EU)
|
|
10880
|
+
if (/privacy\s+policy|gdpr|data\s+protection/i.test(text)) {
|
|
10881
|
+
flags.push('gdpr_mentions');
|
|
10882
|
+
}
|
|
10883
|
+
|
|
10884
|
+
if (/consent|opt.?in|permission/i.test(text)) {
|
|
10885
|
+
flags.push('consent_mentions');
|
|
10886
|
+
}
|
|
10887
|
+
|
|
10888
|
+
return flags;
|
|
10889
|
+
}
|
|
10890
|
+
|
|
10891
|
+
function getDefaultEmailDetection(name, email, emailBody, emailSubject, error = null) {
|
|
10892
|
+
const localAnalysis = analyzeEmailLocally(emailBody, emailSubject, email, name);
|
|
10893
|
+
|
|
10894
|
+
// Determine type based on local analysis
|
|
10895
|
+
let emailType = 'ambiguous';
|
|
10896
|
+
if (localAnalysis.spamScore > 70) emailType = 'spam';
|
|
10897
|
+
else if (localAnalysis.promotionalScore > localAnalysis.transactionalScore && localAnalysis.promotionalScore > 30) emailType = 'promotional';
|
|
10898
|
+
else if (localAnalysis.transactionalScore > 40) emailType = 'transactional';
|
|
10899
|
+
|
|
10900
|
+
const spamConfidence = localAnalysis.spamScore;
|
|
10901
|
+
const phishingRisk = localAnalysis.phishingScore > 50 ? 'medium' : 'low';
|
|
10902
|
+
|
|
10903
|
+
return {
|
|
10904
|
+
success: false,
|
|
10905
|
+
email_type: emailType,
|
|
10906
|
+
email_type_confidence: Math.max(50, spamConfidence),
|
|
10907
|
+
email_subtype: 'personal_message',
|
|
10908
|
+
|
|
10909
|
+
spam_confidence: spamConfidence,
|
|
10910
|
+
phishing_risk: phishingRisk,
|
|
10911
|
+
malware_risk: 'none',
|
|
10912
|
+
|
|
10913
|
+
is_unsolicited: true,
|
|
10914
|
+
is_commercial: localAnalysis.promotionalScore > 30,
|
|
10915
|
+
is_urgent: localAnalysis.urgencyCount > 0,
|
|
10916
|
+
is_personalized: localAnalysis.personalizationScore > 20,
|
|
10917
|
+
|
|
10918
|
+
sender_legitimacy: 'unknown',
|
|
10919
|
+
sender_intent: 'unknown',
|
|
10920
|
+
|
|
10921
|
+
contains_links: localAnalysis.linkCount > 0,
|
|
10922
|
+
contains_attachments: false,
|
|
10923
|
+
contains_unsubscribe: localAnalysis.hasUnsubscribe,
|
|
10924
|
+
contains_phone_number: localAnalysis.hasPhone,
|
|
10925
|
+
contains_address: localAnalysis.hasAddress,
|
|
10926
|
+
|
|
10927
|
+
language_quality: localAnalysis.misspellingCount > 3 ? 'poor' : 'average',
|
|
10928
|
+
urgency_level: localAnalysis.urgencyCount > 2 ? 'high' : 'low',
|
|
10929
|
+
personalization_score: localAnalysis.personalizationScore,
|
|
10930
|
+
|
|
10931
|
+
suggested_action: emailType === 'spam' ? 'mark_as_spam' : 'keep_in_inbox',
|
|
10932
|
+
auto_filter_suggestion: emailType === 'spam' ? 'spam_filter' : 'no_filter',
|
|
10933
|
+
inbox_priority: emailType === 'spam' ? 'ignore' : 'normal',
|
|
10934
|
+
|
|
10935
|
+
key_indicators: ['Local analysis only - API failed'],
|
|
10936
|
+
risk_factors: [],
|
|
10937
|
+
legitimacy_signals: [],
|
|
10938
|
+
|
|
10939
|
+
analysis_timestamp: new Date().toISOString(),
|
|
10940
|
+
content_length: emailBody.length,
|
|
10941
|
+
analysis_complexity: 'simple',
|
|
10942
|
+
|
|
10943
|
+
// Enhanced fields
|
|
10944
|
+
local_analysis: localAnalysis,
|
|
10945
|
+
sender_analysis: analyzeSender(email, name, emailBody),
|
|
10946
|
+
content_stats: {
|
|
10947
|
+
word_count: (emailBody.match(/\S+/g) || []).length,
|
|
10948
|
+
sentence_count: (emailBody.match(/[.!?]+/g) || []).length,
|
|
10949
|
+
link_count: localAnalysis.linkCount,
|
|
10950
|
+
uppercase_ratio: calculateUppercaseRatio(emailBody),
|
|
10951
|
+
exclamation_count: (emailBody.match(/!/g) || []).length,
|
|
10952
|
+
dollar_sign_count: (emailBody.match(/\$/g) || []).length,
|
|
10953
|
+
phone_patterns: (emailBody.match(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g) || []).length,
|
|
10954
|
+
},
|
|
10955
|
+
|
|
10956
|
+
comprehensive_risk_score: calculateComprehensiveRisk({ email_type: emailType, phishing_risk: phishingRisk, is_unsolicited: true, is_urgent: localAnalysis.urgencyCount > 0 }, localAnalysis),
|
|
10957
|
+
|
|
10958
|
+
final_confidence: Math.max(50, spamConfidence),
|
|
10959
|
+
explanation: 'Default analysis due to API failure',
|
|
10960
|
+
error: error || 'API call failed',
|
|
10961
|
+
};
|
|
10962
|
+
}
|
|
10963
|
+
|
|
10964
|
+
const detection_ret = await submit_chat_gpt_prompt({
|
|
10965
|
+
uid,
|
|
10966
|
+
prompt: `Analyze this email and determine if it's SPAM, PROMOTIONAL, TRANSACTIONAL, or PERSONAL/BUSINESS communication.
|
|
10967
|
+
|
|
10968
|
+
FROM: "${name}" <${email}>
|
|
10969
|
+
SUBJECT: "${email_subject}"
|
|
10970
|
+
BODY: "${email_body.substring(0, 800)}${email_body.length > 800 ? '...' : ''}"
|
|
10971
|
+
|
|
10972
|
+
ANALYSIS CRITERIA:
|
|
10973
|
+
|
|
10974
|
+
SPAM INDICATORS:
|
|
10975
|
+
- Unsolicited commercial content
|
|
10976
|
+
- Phishing attempts or suspicious links
|
|
10977
|
+
- Poor grammar/spelling
|
|
10978
|
+
- Urgent/panic-inducing language
|
|
10979
|
+
- Requests for personal information
|
|
10980
|
+
- Too good to be true offers
|
|
10981
|
+
- Hidden/unclear sender identity
|
|
10982
|
+
- Multiple recipient addresses visible
|
|
10983
|
+
- Stock/"spammy" subject lines
|
|
10984
|
+
|
|
10985
|
+
PROMOTIONAL INDICATORS:
|
|
10986
|
+
- Marketing/sales content
|
|
10987
|
+
- Product announcements
|
|
10988
|
+
- Newsletter content
|
|
10989
|
+
- Discount/coupon offers
|
|
10990
|
+
- Event invitations (commercial)
|
|
10991
|
+
- Company updates (marketing focused)
|
|
10992
|
+
- Clear opt-out/unsubscribe option
|
|
10993
|
+
- Professional branding/templates
|
|
10994
|
+
- Call-to-action buttons
|
|
10995
|
+
|
|
10996
|
+
TRANSACTIONAL INDICATORS:
|
|
10997
|
+
- Order confirmations
|
|
10998
|
+
- Shipping notifications
|
|
10999
|
+
- Invoice/billing
|
|
11000
|
+
- Account notifications
|
|
11001
|
+
- Password resets
|
|
11002
|
+
- Booking confirmations
|
|
11003
|
+
- Appointment reminders
|
|
11004
|
+
- Payment receipts
|
|
11005
|
+
- Service updates
|
|
11006
|
+
|
|
11007
|
+
PERSONAL/BUSINESS INDICATORS:
|
|
11008
|
+
- Direct communication to you
|
|
11009
|
+
- Known sender relationship
|
|
11010
|
+
- Work/project related
|
|
11011
|
+
- Personal conversations
|
|
11012
|
+
- One-on-one correspondence
|
|
11013
|
+
- Contains your name specifically
|
|
11014
|
+
- Contextually relevant to you
|
|
11015
|
+
|
|
11016
|
+
ADDITIONAL FACTORS TO CONSIDER:
|
|
11017
|
+
- Sender reputation (company vs personal)
|
|
11018
|
+
- Your relationship with sender
|
|
11019
|
+
- Email formatting quality
|
|
11020
|
+
- Personalization level
|
|
11021
|
+
- Expected vs unexpected content
|
|
11022
|
+
- Action requested (if any)
|
|
11023
|
+
|
|
11024
|
+
PROVIDE DETAILED ANALYSIS WITH CONFIDENCE SCORES.`,
|
|
11025
|
+
model: 'gpt-5-nano',
|
|
11026
|
+
response_format: z.object({
|
|
11027
|
+
// Primary Classification
|
|
11028
|
+
email_type: z.enum(['spam', 'promotional', 'transactional', 'personal', 'business', 'ambiguous']),
|
|
11029
|
+
email_type_confidence: z.number().min(0).max(100),
|
|
11030
|
+
|
|
11031
|
+
// Sub-classification
|
|
11032
|
+
email_subtype: z.enum([
|
|
11033
|
+
// Spam types
|
|
11034
|
+
'phishing_attempt',
|
|
11035
|
+
'scam_offer',
|
|
11036
|
+
'malware_risk',
|
|
11037
|
+
'adult_content',
|
|
11038
|
+
'financial_scam',
|
|
11039
|
+
'lottery_scam',
|
|
11040
|
+
'inheritance_scam',
|
|
11041
|
+
'romance_scam',
|
|
11042
|
+
'tech_support_scam',
|
|
11043
|
+
|
|
11044
|
+
// Promotional types
|
|
11045
|
+
'marketing_newsletter',
|
|
11046
|
+
'product_announcement',
|
|
11047
|
+
'discount_offer',
|
|
11048
|
+
'event_invitation',
|
|
11049
|
+
'company_update',
|
|
11050
|
+
'blog_newsletter',
|
|
11051
|
+
'educational_content',
|
|
11052
|
+
'lead_magnet',
|
|
11053
|
+
|
|
11054
|
+
// Transactional types
|
|
11055
|
+
'order_confirmation',
|
|
11056
|
+
'shipping_notification',
|
|
11057
|
+
'invoice_billing',
|
|
11058
|
+
'payment_receipt',
|
|
11059
|
+
'account_verification',
|
|
11060
|
+
'password_reset',
|
|
11061
|
+
'appointment_reminder',
|
|
11062
|
+
'service_notification',
|
|
11063
|
+
|
|
11064
|
+
// Personal/Business types
|
|
11065
|
+
'personal_message',
|
|
11066
|
+
'work_collaboration',
|
|
11067
|
+
'client_communication',
|
|
11068
|
+
'team_announcement',
|
|
11069
|
+
'meeting_invitation',
|
|
11070
|
+
'project_update',
|
|
11071
|
+
'direct_inquiry',
|
|
11072
|
+
'networking_request',
|
|
11073
|
+
]),
|
|
11074
|
+
|
|
11075
|
+
// Risk Assessment
|
|
11076
|
+
spam_confidence: z.number().min(0).max(100).describe('Confidence this is spam'),
|
|
11077
|
+
phishing_risk: z.enum(['none', 'low', 'medium', 'high', 'critical']),
|
|
11078
|
+
malware_risk: z.enum(['none', 'low', 'medium', 'high']),
|
|
11079
|
+
|
|
11080
|
+
// Content Analysis
|
|
11081
|
+
is_unsolicited: z.boolean().describe('Email was not requested/expected'),
|
|
11082
|
+
is_commercial: z.boolean().describe('Contains commercial intent'),
|
|
11083
|
+
is_urgent: z.boolean().describe('Uses urgent/time-sensitive language'),
|
|
11084
|
+
is_personalized: z.boolean().describe('Content is personalized to recipient'),
|
|
11085
|
+
|
|
11086
|
+
// Sender Analysis
|
|
11087
|
+
sender_legitimacy: z.enum(['verified', 'likely_legitimate', 'suspicious', 'likely_fake', 'unknown']),
|
|
11088
|
+
sender_intent: z.enum(['inform', 'sell', 'scam', 'build_relationship', 'request_action', 'unknown']),
|
|
11089
|
+
|
|
11090
|
+
// Content Characteristics
|
|
11091
|
+
contains_links: z.boolean(),
|
|
11092
|
+
contains_attachments: z.boolean(),
|
|
11093
|
+
contains_unsubscribe: z.boolean(),
|
|
11094
|
+
contains_phone_number: z.boolean(),
|
|
11095
|
+
contains_address: z.boolean(),
|
|
11096
|
+
|
|
11097
|
+
// Language Analysis
|
|
11098
|
+
language_quality: z.enum(['excellent', 'good', 'average', 'poor', 'spammy']),
|
|
11099
|
+
urgency_level: z.enum(['none', 'low', 'medium', 'high', 'extreme']),
|
|
11100
|
+
personalization_score: z.number().min(0).max(100).describe('How personalized the content is'),
|
|
11101
|
+
|
|
11102
|
+
// Action Recommendations
|
|
11103
|
+
suggested_action: z.enum(['delete_immediately', 'mark_as_spam', 'move_to_promotions', 'respond_politely', 'follow_up', 'archive', 'keep_in_inbox', 'add_to_contacts', 'report_phishing']),
|
|
11104
|
+
|
|
11105
|
+
// Filter Recommendations
|
|
11106
|
+
auto_filter_suggestion: z.enum(['spam_filter', 'promotions_filter', 'social_filter', 'updates_filter', 'primary_inbox', 'no_filter']),
|
|
11107
|
+
|
|
11108
|
+
// Priority
|
|
11109
|
+
inbox_priority: z.enum(['critical', 'high', 'normal', 'low', 'ignore']),
|
|
11110
|
+
|
|
11111
|
+
// Detailed Analysis
|
|
11112
|
+
key_indicators: z.array(z.string()).describe('Key factors influencing classification'),
|
|
11113
|
+
risk_factors: z.array(z.string()).describe('Specific risks identified'),
|
|
11114
|
+
legitimacy_signals: z.array(z.string()).describe('Signals of legitimate communication'),
|
|
11115
|
+
|
|
11116
|
+
// Metadata
|
|
11117
|
+
analysis_timestamp: z.string(),
|
|
11118
|
+
content_length: z.number().describe('Length of email body analyzed'),
|
|
11119
|
+
analysis_complexity: z.enum(['simple', 'moderate', 'complex', 'ambiguous']),
|
|
11120
|
+
}),
|
|
11121
|
+
metadata: {
|
|
11122
|
+
func: 'detect_email_type',
|
|
11123
|
+
sender_name: name,
|
|
11124
|
+
sender_email: email,
|
|
11125
|
+
subject_length: email_subject?.length || 0,
|
|
11126
|
+
body_length: email_body?.length || 0,
|
|
11127
|
+
body_preview_hash: await hashString(email_body.substring(0, 200)),
|
|
11128
|
+
},
|
|
11129
|
+
account_profile_info,
|
|
11130
|
+
tools: [{ type: 'web_search_preview' }],
|
|
11131
|
+
});
|
|
11132
|
+
|
|
11133
|
+
try {
|
|
11134
|
+
if (detection_ret.code > -1) {
|
|
11135
|
+
const data = typeof detection_ret.data === 'string' ? JSON.parse(detection_ret.data) : detection_ret.data;
|
|
11136
|
+
|
|
11137
|
+
// Enhance with additional analysis
|
|
11138
|
+
const enhancedData = enhanceEmailDetection(data, name, email, email_body, email_subject);
|
|
11139
|
+
|
|
11140
|
+
return {
|
|
11141
|
+
success: true,
|
|
11142
|
+
...enhancedData,
|
|
11143
|
+
};
|
|
11144
|
+
} else {
|
|
11145
|
+
console.error('Email detection API call failed:', detection_ret);
|
|
11146
|
+
return getDefaultEmailDetection(name, email, email_body, email_subject);
|
|
11147
|
+
}
|
|
11148
|
+
} catch (error) {
|
|
11149
|
+
console.error('Error in detect_email_type:', error);
|
|
11150
|
+
return getDefaultEmailDetection(name, email, email_body, email_subject, error.message);
|
|
11151
|
+
}
|
|
11152
|
+
};
|