@xuda.io/ai_module 1.1.4680 → 1.1.4682

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