nolimit-x 1.0.58 → 1.0.61
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/package.json +1 -1
- package/src/cli.js +14 -3
- package/src/email-validator.js +26 -12
- package/src/sender.js +2 -2
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -502,11 +502,21 @@ program
|
|
|
502
502
|
process.exit(1);
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
-
|
|
505
|
+
// Parse lines — support both "email" and "email,Name" formats
|
|
506
|
+
const rawLines = fs.readFileSync(inputPath, 'utf8')
|
|
506
507
|
.split('\n')
|
|
507
508
|
.map(l => l.trim())
|
|
508
509
|
.filter(l => l && l.includes('@'));
|
|
509
510
|
|
|
511
|
+
// Build a map of email → original line (preserving name data)
|
|
512
|
+
const originalLineMap = new Map();
|
|
513
|
+
const emails = rawLines.map(line => {
|
|
514
|
+
const commaIdx = line.indexOf(',');
|
|
515
|
+
const email = commaIdx !== -1 ? line.substring(0, commaIdx).trim() : line;
|
|
516
|
+
originalLineMap.set(email, line);
|
|
517
|
+
return email;
|
|
518
|
+
});
|
|
519
|
+
|
|
510
520
|
const threshold = parseInt(options.threshold);
|
|
511
521
|
const concurrency = parseInt(options.concurrency);
|
|
512
522
|
|
|
@@ -550,8 +560,9 @@ program
|
|
|
550
560
|
const safePath = path.join(validateDir, 'safe.txt');
|
|
551
561
|
const unsafePath = path.join(validateDir, 'unsafe.txt');
|
|
552
562
|
|
|
553
|
-
|
|
554
|
-
fs.writeFileSync(
|
|
563
|
+
// Write back original lines (with name if present) to preserve lead data
|
|
564
|
+
fs.writeFileSync(safePath, results.safe.map(r => originalLineMap.get(r.email) || r.email).join('\n') + '\n');
|
|
565
|
+
fs.writeFileSync(unsafePath, results.rejected.map(r => `${originalLineMap.get(r.email) || r.email} ; ${r.reasons.join(', ')}`).join('\n') + '\n');
|
|
555
566
|
|
|
556
567
|
console.log(`\n${WHITE}Total:${RESET} ${BOLD}${results.stats.total}${RESET}`);
|
|
557
568
|
console.log(`${WHITE}Time:${RESET} ${BOLD}${elapsed}s${RESET}`);
|
package/src/email-validator.js
CHANGED
|
@@ -26,44 +26,58 @@ const {
|
|
|
26
26
|
// ═══════════════════════════════════════════════════════════
|
|
27
27
|
const domainCache = new Map();
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Retry wrapper with exponential backoff for DNS queries.
|
|
31
|
+
* Retries up to `maxRetries` times on transient failures.
|
|
32
|
+
*/
|
|
33
|
+
async function withRetry(fn, maxRetries = 3, baseDelay = 300) {
|
|
34
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
35
|
+
try {
|
|
36
|
+
return await fn();
|
|
37
|
+
} catch (e) {
|
|
38
|
+
// ENOTFOUND / ENODATA = domain genuinely doesn't exist, no point retrying
|
|
39
|
+
if (e.code === 'ENOTFOUND' || e.code === 'ENODATA') return null;
|
|
40
|
+
if (attempt === maxRetries) return null;
|
|
41
|
+
// Exponential backoff with jitter
|
|
42
|
+
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 100;
|
|
43
|
+
await new Promise(r => setTimeout(r, delay));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
29
49
|
/**
|
|
30
50
|
* Resolve MX records for a domain.
|
|
31
51
|
* Returns the lowest-priority MX hostname (primary).
|
|
32
52
|
*/
|
|
33
53
|
async function getMX(domain) {
|
|
34
|
-
|
|
54
|
+
return withRetry(async () => {
|
|
35
55
|
const records = await dns.resolveMx(domain);
|
|
36
56
|
if (!records || records.length === 0) return null;
|
|
37
57
|
// Sort by priority (lowest = primary)
|
|
38
58
|
records.sort((a, b) => a.priority - b.priority);
|
|
39
59
|
return records[0].exchange.toLowerCase().replace(/\.$/, '');
|
|
40
|
-
}
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
60
|
+
});
|
|
43
61
|
}
|
|
44
62
|
|
|
45
63
|
/**
|
|
46
64
|
* Resolve A record (hostname → IP).
|
|
47
65
|
*/
|
|
48
66
|
async function getA(hostname) {
|
|
49
|
-
|
|
67
|
+
return withRetry(async () => {
|
|
50
68
|
const addrs = await dns.resolve4(hostname);
|
|
51
69
|
return addrs && addrs.length > 0 ? addrs[0] : null;
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
70
|
+
});
|
|
55
71
|
}
|
|
56
72
|
|
|
57
73
|
/**
|
|
58
74
|
* Reverse PTR lookup (IP → hostname).
|
|
59
75
|
*/
|
|
60
76
|
async function getPTR(ip) {
|
|
61
|
-
|
|
77
|
+
return withRetry(async () => {
|
|
62
78
|
const names = await dns.reverse(ip);
|
|
63
79
|
return names && names.length > 0 ? names[0].toLowerCase() : null;
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
80
|
+
});
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
/**
|
package/src/sender.js
CHANGED
|
@@ -492,6 +492,8 @@ async function send(options) {
|
|
|
492
492
|
try { senderIntel.recordSuccessfulSend(campaignId, sender, email, result); } catch (e) { }
|
|
493
493
|
} else {
|
|
494
494
|
campaign.failedSends++;
|
|
495
|
+
const errMsg = result.error || result.message || 'unknown error';
|
|
496
|
+
console.log(`${RED}✗${RESET} ${WHITE}${email}${RESET} — ${RED}${errMsg}${RESET}`);
|
|
495
497
|
try { senderIntel.recordFailedSend(campaignId, sender, email, result); } catch (e) { }
|
|
496
498
|
}
|
|
497
499
|
campaign.processedEmails++;
|
|
@@ -1152,13 +1154,11 @@ function buildBasicRustJob(config, recipient, messageHtml, configManager) {
|
|
|
1152
1154
|
// Scenario 2: Use config from_email
|
|
1153
1155
|
fromEmail = conf.from_email;
|
|
1154
1156
|
envelopeSender = conf.from_email;
|
|
1155
|
-
console.log(`[SENDER] Using config from_email: ${fromEmail}`);
|
|
1156
1157
|
} else {
|
|
1157
1158
|
// Scenario 3: Use SMTP user email
|
|
1158
1159
|
const smtpConfig = Array.isArray(config.smtp) ? config.smtp[0] : config.smtp;
|
|
1159
1160
|
fromEmail = smtpConfig.user;
|
|
1160
1161
|
envelopeSender = smtpConfig.user;
|
|
1161
|
-
console.log(`[SENDER] Using SMTP user email: ${fromEmail}`);
|
|
1162
1162
|
}
|
|
1163
1163
|
}
|
|
1164
1164
|
|