@xuda.io/ai_module 1.1.4711 → 1.1.4713

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