@xuda.io/account_module 1.2.1839 → 1.2.1841
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.mjs +116 -4
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -2153,9 +2153,7 @@ export const add_contact = async function (req, job_id, headers) {
|
|
|
2153
2153
|
|
|
2154
2154
|
set_account_profile_picture(uid, contact_uid, metadata, job_id, headers, account_profile_info);
|
|
2155
2155
|
} else {
|
|
2156
|
-
|
|
2157
|
-
is_spam = true;
|
|
2158
|
-
}
|
|
2156
|
+
is_spam = isLikelySpamEmail(email);
|
|
2159
2157
|
|
|
2160
2158
|
// contact without use account
|
|
2161
2159
|
cached_contact = await get_xuda_cache(uid, 'contact', email, 'metadata');
|
|
@@ -2165,7 +2163,7 @@ export const add_contact = async function (req, job_id, headers) {
|
|
|
2165
2163
|
// }
|
|
2166
2164
|
|
|
2167
2165
|
if (_.isEmpty(cached_contact)) {
|
|
2168
|
-
if (metadata.subject && !metadata.is_sent && !metadata.is_answered && !metadata.not_junk) {
|
|
2166
|
+
if (!is_spam && metadata.subject && !metadata.is_sent && !metadata.is_answered && !metadata.not_junk) {
|
|
2169
2167
|
// email_type_info = await ai_module.detect_email_type(uid, name, email, account_profile_info, '', metadata?.subject);
|
|
2170
2168
|
// is_spam = email_type_info.email_type === 'spam' || email_type_info.email_type === 'promotional';
|
|
2171
2169
|
is_spam = await ai_module.is_spam_email(uid, email, metadata.subject, account_profile_info);
|
|
@@ -3500,3 +3498,117 @@ export const process_accounts_emails = async function () {
|
|
|
3500
3498
|
email_module.process_emails({ uid: account_doc._id });
|
|
3501
3499
|
}
|
|
3502
3500
|
};
|
|
3501
|
+
|
|
3502
|
+
function isLikelySpamEmail(email) {
|
|
3503
|
+
if (!email || typeof email !== 'string') return true; // Invalid input → treat as spam
|
|
3504
|
+
|
|
3505
|
+
const lower = email.toLowerCase().trim();
|
|
3506
|
+
|
|
3507
|
+
// Basic format validation (must have @ and domain)
|
|
3508
|
+
if (!lower.includes('@') || lower.split('@').length !== 2) return true;
|
|
3509
|
+
|
|
3510
|
+
const [localPart, domain] = lower.split('@');
|
|
3511
|
+
|
|
3512
|
+
if (!localPart || !domain) return true;
|
|
3513
|
+
|
|
3514
|
+
// ────────────────────────────────────────────────
|
|
3515
|
+
// 1. Obvious syntax / format red flags
|
|
3516
|
+
// ────────────────────────────────────────────────
|
|
3517
|
+
if (
|
|
3518
|
+
localPart.length > 64 || // RFC 5321 local-part max
|
|
3519
|
+
domain.length > 255 || // RFC 5321 domain max
|
|
3520
|
+
localPart.startsWith('.') ||
|
|
3521
|
+
localPart.endsWith('.') ||
|
|
3522
|
+
localPart.includes('..') ||
|
|
3523
|
+
domain.includes('..') ||
|
|
3524
|
+
domain.startsWith('-') ||
|
|
3525
|
+
domain.endsWith('-') ||
|
|
3526
|
+
localPart.match(/[^\w.!#$%&'*+/=?^`{|}~-]/) !== null // illegal chars in local part
|
|
3527
|
+
) {
|
|
3528
|
+
return true;
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
// ────────────────────────────────────────────────
|
|
3532
|
+
// 2. Very common spam patterns in local part
|
|
3533
|
+
// ────────────────────────────────────────────────
|
|
3534
|
+
const spamPatterns = [
|
|
3535
|
+
/\d{8,}/, // long number strings (e.g. 1234567890user)
|
|
3536
|
+
/[a-z]{10,}/, // very long random letters
|
|
3537
|
+
/unsubscribe/i, // rare but classic spam sign
|
|
3538
|
+
/free|win|bonus|deal|promo/i, // promotional keywords in address
|
|
3539
|
+
/\+\d+$/, // + followed only by numbers (often disposable)
|
|
3540
|
+
/^[\d\W_]+$/, // only digits & symbols (very suspicious)
|
|
3541
|
+
];
|
|
3542
|
+
|
|
3543
|
+
if (spamPatterns.some((p) => p.test(localPart))) {
|
|
3544
|
+
return true;
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
// ────────────────────────────────────────────────
|
|
3548
|
+
// 3. Common free / disposable / temporary providers
|
|
3549
|
+
// (update this list periodically – 2026 common ones)
|
|
3550
|
+
// ────────────────────────────────────────────────
|
|
3551
|
+
const freeOrDisposableDomains = new Set([
|
|
3552
|
+
'gmail.com',
|
|
3553
|
+
'yahoo.com',
|
|
3554
|
+
'hotmail.com',
|
|
3555
|
+
'outlook.com',
|
|
3556
|
+
'aol.com',
|
|
3557
|
+
'icloud.com',
|
|
3558
|
+
'proton.me',
|
|
3559
|
+
'protonmail.com',
|
|
3560
|
+
'mail.com',
|
|
3561
|
+
'yandex.com',
|
|
3562
|
+
'ymail.com',
|
|
3563
|
+
'rocketmail.com',
|
|
3564
|
+
'gmx.com',
|
|
3565
|
+
'gmx.net',
|
|
3566
|
+
'web.de',
|
|
3567
|
+
'live.com',
|
|
3568
|
+
'msn.com',
|
|
3569
|
+
'inbox.com',
|
|
3570
|
+
'zoho.com',
|
|
3571
|
+
'tutanota.com',
|
|
3572
|
+
'tempmail.com',
|
|
3573
|
+
'10minutemail.com',
|
|
3574
|
+
'guerrillamail.com',
|
|
3575
|
+
'trashmail.com',
|
|
3576
|
+
'mailinator.com',
|
|
3577
|
+
'yopmail.com',
|
|
3578
|
+
'sharklasers.com',
|
|
3579
|
+
// Add more from disposable lists if needed
|
|
3580
|
+
]);
|
|
3581
|
+
|
|
3582
|
+
if (freeOrDisposableDomains.has(domain)) {
|
|
3583
|
+
// Still allow if it looks like a serious role or branded use
|
|
3584
|
+
const seriousPrefixes = ['info', 'support', 'sales', 'contact', 'hello', 'team', 'admin', 'office', 'billing'];
|
|
3585
|
+
const prefix = localPart.split('+')[0].split('.')[0]; // rough prefix
|
|
3586
|
+
|
|
3587
|
+
if (!seriousPrefixes.some((p) => prefix.includes(p))) {
|
|
3588
|
+
// Extra lenient length check for free providers
|
|
3589
|
+
if (localPart.length > 24 || localPart.match(/\d{6,}/)) {
|
|
3590
|
+
return true;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
// Otherwise: free provider but looks somewhat legitimate → not spam
|
|
3594
|
+
return false;
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
// ────────────────────────────────────────────────
|
|
3598
|
+
// 4. Very short / meaningless / bot-like local parts
|
|
3599
|
+
// ────────────────────────────────────────────────
|
|
3600
|
+
if (localPart.length < 3 || localPart.match(/^[a-z\d]{1,4}$/)) {
|
|
3601
|
+
return true; // e.g. a1@b.com, xyz@temp.com
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
// ────────────────────────────────────────────────
|
|
3605
|
+
// 5. Suspicious TLDs sometimes used in spam (2026 context)
|
|
3606
|
+
// ────────────────────────────────────────────────
|
|
3607
|
+
const suspiciousTlds = ['.top', '.xyz', '.club', '.online', '.site', '.fun', '.buzz'];
|
|
3608
|
+
if (suspiciousTlds.some((tld) => domain.endsWith(tld))) {
|
|
3609
|
+
return true;
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
// Passed most checks → probably not spam
|
|
3613
|
+
return false;
|
|
3614
|
+
}
|