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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nolimit-x",
3
- "version": "1.0.58",
3
+ "version": "1.0.61",
4
4
  "description": "Advanced email sender ",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -502,11 +502,21 @@ program
502
502
  process.exit(1);
503
503
  }
504
504
 
505
- const emails = fs.readFileSync(inputPath, 'utf8')
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
- fs.writeFileSync(safePath, results.safe.map(r => r.email).join('\n') + '\n');
554
- fs.writeFileSync(unsafePath, results.rejected.map(r => `${r.email} ; ${r.reasons.join(', ')}`).join('\n') + '\n');
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}`);
@@ -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
- try {
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
- } catch (e) {
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
- try {
67
+ return withRetry(async () => {
50
68
  const addrs = await dns.resolve4(hostname);
51
69
  return addrs && addrs.length > 0 ? addrs[0] : null;
52
- } catch (e) {
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
- try {
77
+ return withRetry(async () => {
62
78
  const names = await dns.reverse(ip);
63
79
  return names && names.length > 0 ? names[0].toLowerCase() : null;
64
- } catch (e) {
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