nolimit-x 1.0.59 → 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.59",
3
+ "version": "1.0.61",
4
4
  "description": "Advanced email sender ",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -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