nolimit-x 1.0.0
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/bin/nolimit +3 -0
- package/package.json +18 -0
- package/src/attachment-handler.js +253 -0
- package/src/cli.js +31 -0
- package/src/document-generator.js +275 -0
- package/src/encryption.js +125 -0
- package/src/init.js +79 -0
- package/src/multi-handler.js +184 -0
- package/src/obfuscator.js +350 -0
- package/src/placeholders.js +230 -0
- package/src/processor.js +542 -0
- package/src/qr-generator.js +222 -0
- package/src/sender.js +41 -0
- package/src/utils.js +374 -0
- package/templates/config.json +54 -0
- package/templates/emails.txt +3 -0
- package/templates/messages.html +5 -0
- package/templates/senders.txt +3 -0
- package/test-campaign/config.json +35 -0
- package/test-campaign/emails.txt +3 -0
- package/test-campaign/messages.html +5 -0
- package/test-campaign/senders.txt +3 -0
- package/test-raw-smtp/config.json +56 -0
- package/test-raw-smtp/emails.txt +7 -0
- package/test-raw-smtp/messages.html +5 -0
- package/test-raw-smtp/senders.txt +7 -0
package/src/processor.js
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const utils = require('./utils');
|
|
5
|
+
const placeholders = require('./placeholders');
|
|
6
|
+
const MessageEncryption = require('./encryption');
|
|
7
|
+
const MultiHandler = require('./multi-handler');
|
|
8
|
+
const AttachmentHandler = require('./attachment-handler');
|
|
9
|
+
const DocumentGenerator = require('./document-generator');
|
|
10
|
+
const QRGenerator = require('./qr-generator');
|
|
11
|
+
const Obfuscator = require('./obfuscator');
|
|
12
|
+
const pMap = require('p-map');
|
|
13
|
+
|
|
14
|
+
// Configuration management
|
|
15
|
+
class ConfigManager {
|
|
16
|
+
constructor(configPath = './config.json') {
|
|
17
|
+
this.configPath = configPath;
|
|
18
|
+
this.config = null;
|
|
19
|
+
this.smtpConfig = null;
|
|
20
|
+
this.emailList = [];
|
|
21
|
+
this.sendersList = [];
|
|
22
|
+
this.messageBody = '';
|
|
23
|
+
this.attachments = [];
|
|
24
|
+
this.faviconCache = {};
|
|
25
|
+
this.turbo = false;
|
|
26
|
+
this.concurrency = 2;
|
|
27
|
+
|
|
28
|
+
// Initialize new handlers
|
|
29
|
+
this.encryption = new MessageEncryption();
|
|
30
|
+
this.multiHandler = new MultiHandler();
|
|
31
|
+
this.attachmentHandler = new AttachmentHandler();
|
|
32
|
+
|
|
33
|
+
// Initialize Priority 2 handlers
|
|
34
|
+
this.documentGenerator = new DocumentGenerator();
|
|
35
|
+
this.qrGenerator = new QRGenerator();
|
|
36
|
+
this.obfuscator = new Obfuscator();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Load configuration from file
|
|
40
|
+
loadConfig() {
|
|
41
|
+
try {
|
|
42
|
+
if (!fs.existsSync(this.configPath)) {
|
|
43
|
+
throw new Error(`Config file not found: ${this.configPath}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.config = JSON.parse(fs.readFileSync(this.configPath, 'utf8'));
|
|
47
|
+
this.smtpConfig = this.config.smtp[0];
|
|
48
|
+
|
|
49
|
+
// Turbo mode logic
|
|
50
|
+
this.turbo = !!this.config.configurations?.system?.turbo;
|
|
51
|
+
this.concurrency = this.turbo ? Math.max(4, os.cpus().length) : 2;
|
|
52
|
+
|
|
53
|
+
console.log(`✓ Configuration loaded from ${this.configPath}`);
|
|
54
|
+
console.log(`✓ Turbo mode: ${this.turbo ? 'ON' : 'OFF'} (Concurrency: ${this.concurrency})`);
|
|
55
|
+
return true;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(`✗ Failed to load config: ${error.message}`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Load email list
|
|
63
|
+
loadEmailList() {
|
|
64
|
+
try {
|
|
65
|
+
const emailPath = this.config.emails_list.path;
|
|
66
|
+
if (!fs.existsSync(emailPath)) {
|
|
67
|
+
throw new Error(`Email list file not found: ${emailPath}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const content = fs.readFileSync(emailPath, 'utf8');
|
|
71
|
+
this.emailList = content.split(os.EOL)
|
|
72
|
+
.filter(email => email.trim() && !email.startsWith('#') && utils.isValidEmail(email.trim()));
|
|
73
|
+
|
|
74
|
+
console.log(`✓ Loaded ${this.emailList.length} valid email addresses`);
|
|
75
|
+
return true;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error(`✗ Failed to load email list: ${error.message}`);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Load senders list
|
|
83
|
+
loadSendersList() {
|
|
84
|
+
try {
|
|
85
|
+
const sendersPath = this.config.senders_list.path;
|
|
86
|
+
if (!fs.existsSync(sendersPath)) {
|
|
87
|
+
throw new Error(`Senders list file not found: ${sendersPath}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const content = fs.readFileSync(sendersPath, 'utf8');
|
|
91
|
+
this.sendersList = content.split(os.EOL)
|
|
92
|
+
.filter(sender => sender.trim() && !sender.startsWith('#') && utils.isValidEmail(sender.trim()));
|
|
93
|
+
|
|
94
|
+
console.log(`✓ Loaded ${this.sendersList.length} valid sender addresses`);
|
|
95
|
+
|
|
96
|
+
// Load into multi-handler for rotation
|
|
97
|
+
this.multiHandler.loadSenders(sendersPath);
|
|
98
|
+
|
|
99
|
+
return true;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(`✗ Failed to load senders list: ${error.message}`);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Load message body
|
|
107
|
+
loadMessageBody() {
|
|
108
|
+
try {
|
|
109
|
+
const messagePath = this.config.messages_body.path;
|
|
110
|
+
if (!fs.existsSync(messagePath)) {
|
|
111
|
+
throw new Error(`Message body file not found: ${messagePath}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.messageBody = fs.readFileSync(messagePath, 'utf8');
|
|
115
|
+
console.log(`✓ Loaded message body from ${messagePath}`);
|
|
116
|
+
|
|
117
|
+
// Load additional multi-content if configured
|
|
118
|
+
this.loadMultiContent();
|
|
119
|
+
|
|
120
|
+
return true;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(`✗ Failed to load message body: ${error.message}`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Load multiple content files
|
|
128
|
+
loadMultiContent() {
|
|
129
|
+
try {
|
|
130
|
+
// Load subjects if multiple subjects is enabled
|
|
131
|
+
if (this.config.configurations.agent.multiple_subjects && this.config.subjects_list) {
|
|
132
|
+
this.multiHandler.loadSubjects(this.config.subjects_list.path);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Load from names if multiple from names is enabled
|
|
136
|
+
if (this.config.configurations.agent.multiple_from_names && this.config.froms_list) {
|
|
137
|
+
this.multiHandler.loadFromNames(this.config.froms_list.path);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Load links if multiple links is enabled
|
|
141
|
+
if (this.config.configurations.agent.multiple_links && this.config.links_list) {
|
|
142
|
+
this.multiHandler.loadLinks(this.config.links_list.path);
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.warn(`Failed to load some multi-content: ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Load attachments
|
|
150
|
+
loadAttachments() {
|
|
151
|
+
try {
|
|
152
|
+
if (!this.config.configurations.use_attachment) {
|
|
153
|
+
console.log('✓ Attachments disabled in configuration');
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.attachments = [];
|
|
158
|
+
for (const attachment of this.config.attachments) {
|
|
159
|
+
if (attachment.active && fs.existsSync(attachment.path)) {
|
|
160
|
+
this.attachments.push({
|
|
161
|
+
filename: attachment.filename,
|
|
162
|
+
path: attachment.path,
|
|
163
|
+
active: attachment.active,
|
|
164
|
+
obfuscate: attachment.obfuscate || false,
|
|
165
|
+
encrypted: attachment.encrypted || false,
|
|
166
|
+
scripter: attachment.scripter || false
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Get attachment statistics
|
|
172
|
+
const stats = this.attachmentHandler.getAttachmentStats(this.config.attachments);
|
|
173
|
+
console.log(`✓ Loaded ${stats.active} active attachments (${stats.encrypted} encrypted, ${stats.obfuscated} obfuscated, ${stats.scripted} scripted)`);
|
|
174
|
+
|
|
175
|
+
return true;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(`✗ Failed to load attachments: ${error.message}`);
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Get random sender
|
|
183
|
+
getRandomSender() {
|
|
184
|
+
// Use multi-handler if multiple senders is enabled
|
|
185
|
+
if (this.config.configurations.agent.multiple_senders) {
|
|
186
|
+
const sender = this.multiHandler.getRandomSender();
|
|
187
|
+
if (sender) return sender;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Fallback to original method
|
|
191
|
+
if (this.sendersList.length === 0) {
|
|
192
|
+
return this.config.configurations.from_email;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const randomIndex = Math.floor(Math.random() * this.sendersList.length);
|
|
196
|
+
return this.sendersList[randomIndex];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Get next sender (round-robin)
|
|
200
|
+
getNextSender() {
|
|
201
|
+
if (this.config.configurations.agent.multiple_senders) {
|
|
202
|
+
const sender = this.multiHandler.getNextSender();
|
|
203
|
+
if (sender) return sender;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return this.getRandomSender();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Get subject
|
|
210
|
+
getSubject() {
|
|
211
|
+
if (this.config.configurations.agent.multiple_subjects) {
|
|
212
|
+
const subject = this.multiHandler.getRandomSubject();
|
|
213
|
+
if (subject) return subject;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return this.config.configurations.message.mail_subject;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Get from name
|
|
220
|
+
getFromName() {
|
|
221
|
+
if (this.config.configurations.agent.multiple_from_names) {
|
|
222
|
+
const fromName = this.multiHandler.getRandomFromName();
|
|
223
|
+
if (fromName) return fromName;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return this.config.configurations.message.from_name;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Priority 2: Document Generation
|
|
230
|
+
async generateDocument(htmlContent, format = 'pdf', options = {}) {
|
|
231
|
+
try {
|
|
232
|
+
switch (format.toLowerCase()) {
|
|
233
|
+
case 'pdf':
|
|
234
|
+
return await this.documentGenerator.htmlToPdf(htmlContent, options);
|
|
235
|
+
case 'docx':
|
|
236
|
+
case 'doc':
|
|
237
|
+
case 'rtf':
|
|
238
|
+
return await this.documentGenerator.htmlToDoc(htmlContent, format, options);
|
|
239
|
+
case 'epub':
|
|
240
|
+
return await this.documentGenerator.htmlToEpub(htmlContent, options);
|
|
241
|
+
case 'xlsx':
|
|
242
|
+
return await this.documentGenerator.htmlToXlsx(htmlContent);
|
|
243
|
+
case 'pptx':
|
|
244
|
+
return await this.documentGenerator.htmlToPptx(htmlContent, options);
|
|
245
|
+
default:
|
|
246
|
+
console.warn(`Unsupported document format: ${format}`);
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error(`Document generation failed: ${error.message}`);
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Priority 2: QR Code Generation
|
|
256
|
+
async generateQRCode(data, purpose = 'general', options = {}) {
|
|
257
|
+
try {
|
|
258
|
+
return await this.qrGenerator.generateMultiPurposeQR(data, purpose, options);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error(`QR code generation failed: ${error.message}`);
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Priority 2: Embed QR code in message
|
|
266
|
+
embedQRInMessage(messageHtml, qrDataURL, position = 'bottom', size = 'medium') {
|
|
267
|
+
return this.qrGenerator.embedQRInMessage(messageHtml, qrDataURL, position, size);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Priority 2: Obfuscation
|
|
271
|
+
obfuscateContent(content, method = 'base64', options = {}) {
|
|
272
|
+
try {
|
|
273
|
+
return this.obfuscator.obfuscate(content, method, options);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error(`Obfuscation failed: ${error.message}`);
|
|
276
|
+
return content; // Return original content if obfuscation fails
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Priority 2: Deobfuscation
|
|
281
|
+
deobfuscateContent(content, method = 'base64', options = {}) {
|
|
282
|
+
try {
|
|
283
|
+
return this.obfuscator.deobfuscate(content, method, options);
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error(`Deobfuscation failed: ${error.message}`);
|
|
286
|
+
return content; // Return original content if deobfuscation fails
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Priority 2: Obfuscate URLs
|
|
291
|
+
obfuscateURL(url, method = 'base64') {
|
|
292
|
+
return this.obfuscator.obfuscateURL(url, method);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Priority 2: Obfuscate JavaScript
|
|
296
|
+
obfuscateJavaScript(code, options = {}) {
|
|
297
|
+
return this.obfuscator.obfuscateJavaScript(code, options);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Priority 2: Obfuscate HTML
|
|
301
|
+
obfuscateHTML(html, options = {}) {
|
|
302
|
+
return this.obfuscator.obfuscateHTML(html, options);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Get configuration summary
|
|
306
|
+
getSummary() {
|
|
307
|
+
return {
|
|
308
|
+
smtp: {
|
|
309
|
+
host: this.smtpConfig?.host,
|
|
310
|
+
port: this.smtpConfig?.port,
|
|
311
|
+
secure: this.smtpConfig?.secure,
|
|
312
|
+
user: utils.maskEmail(this.smtpConfig?.user || '')
|
|
313
|
+
},
|
|
314
|
+
campaign: {
|
|
315
|
+
totalEmails: this.emailList.length,
|
|
316
|
+
totalSenders: this.sendersList.length,
|
|
317
|
+
attachments: this.attachments.length,
|
|
318
|
+
useAttachments: this.config.configurations.use_attachment,
|
|
319
|
+
multiThreading: this.config.configurations.agent.is_multi_thread,
|
|
320
|
+
threads: this.config.configurations.agent.how_many_thread,
|
|
321
|
+
delaySending: this.config.configurations.system.delay_sending,
|
|
322
|
+
delaySeconds: this.config.configurations.system.delay_sending_seconds
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Turbo parallel utility
|
|
328
|
+
async turboMap(items, fn) {
|
|
329
|
+
return await pMap(items, fn, { concurrency: this.concurrency });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Campaign processor
|
|
334
|
+
class CampaignProcessor {
|
|
335
|
+
constructor(configManager) {
|
|
336
|
+
this.configManager = configManager;
|
|
337
|
+
this.processedEmails = 0;
|
|
338
|
+
this.successfulSends = 0;
|
|
339
|
+
this.failedSends = 0;
|
|
340
|
+
this.startTime = null;
|
|
341
|
+
this.endTime = null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Turbo mode: send campaign in parallel
|
|
345
|
+
async turboSendCampaign(sendEmailFn) {
|
|
346
|
+
const emails = this.configManager.emailList;
|
|
347
|
+
const turbo = this.configManager.turbo;
|
|
348
|
+
const concurrency = this.configManager.concurrency;
|
|
349
|
+
console.log(`\nTurbo mode is ON. Sending emails in parallel (concurrency: ${concurrency})...`);
|
|
350
|
+
await this.configManager.turboMap(emails, async (email, idx) => {
|
|
351
|
+
try {
|
|
352
|
+
const senderEmail = this.configManager.getNextSender();
|
|
353
|
+
const processed = await this.processEmail(email, senderEmail);
|
|
354
|
+
if (!processed) {
|
|
355
|
+
this.failedSends++;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const result = await sendEmailFn(processed);
|
|
359
|
+
if (result === true) {
|
|
360
|
+
this.successfulSends++;
|
|
361
|
+
} else {
|
|
362
|
+
this.failedSends++;
|
|
363
|
+
}
|
|
364
|
+
} catch (err) {
|
|
365
|
+
this.failedSends++;
|
|
366
|
+
console.error(`Turbo send error for ${email}:`, err.message);
|
|
367
|
+
} finally {
|
|
368
|
+
this.processedEmails++;
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Standard mode: send campaign sequentially
|
|
374
|
+
async sequentialSendCampaign(sendEmailFn) {
|
|
375
|
+
const emails = this.configManager.emailList;
|
|
376
|
+
console.log(`\nTurbo mode is OFF. Sending emails sequentially...`);
|
|
377
|
+
for (const email of emails) {
|
|
378
|
+
try {
|
|
379
|
+
const senderEmail = this.configManager.getNextSender();
|
|
380
|
+
const processed = await this.processEmail(email, senderEmail);
|
|
381
|
+
if (!processed) {
|
|
382
|
+
this.failedSends++;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
const result = await sendEmailFn(processed);
|
|
386
|
+
if (result === true) {
|
|
387
|
+
this.successfulSends++;
|
|
388
|
+
} else {
|
|
389
|
+
this.failedSends++;
|
|
390
|
+
}
|
|
391
|
+
} catch (err) {
|
|
392
|
+
this.failedSends++;
|
|
393
|
+
console.error(`Sequential send error for ${email}:`, err.message);
|
|
394
|
+
} finally {
|
|
395
|
+
this.processedEmails++;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Main campaign entry point
|
|
401
|
+
async sendCampaign(sendEmailFn) {
|
|
402
|
+
this.startTimer();
|
|
403
|
+
if (this.configManager.turbo) {
|
|
404
|
+
await this.turboSendCampaign(sendEmailFn);
|
|
405
|
+
} else {
|
|
406
|
+
await this.sequentialSendCampaign(sendEmailFn);
|
|
407
|
+
}
|
|
408
|
+
this.endTimer();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Initialize campaign
|
|
412
|
+
async initialize() {
|
|
413
|
+
console.log('\nInitializing campaign...');
|
|
414
|
+
|
|
415
|
+
if (!this.configManager.loadConfig()) return false;
|
|
416
|
+
if (!this.configManager.loadEmailList()) return false;
|
|
417
|
+
if (!this.configManager.loadSendersList()) return false;
|
|
418
|
+
if (!this.configManager.loadMessageBody()) return false;
|
|
419
|
+
if (!this.configManager.loadAttachments()) return false;
|
|
420
|
+
|
|
421
|
+
// Pre-fetch favicons for all domains to avoid delays during sending
|
|
422
|
+
console.log('\nPre-fetching favicons...');
|
|
423
|
+
const domains = [...new Set(this.configManager.emailList.map(email => utils.getDomainFromEmail(email)))];
|
|
424
|
+
await utils.batchFetchFavicons(domains);
|
|
425
|
+
|
|
426
|
+
const summary = this.configManager.getSummary();
|
|
427
|
+
console.log('\n📊 Campaign Summary:');
|
|
428
|
+
console.log(` SMTP: ${summary.smtp.host}:${summary.smtp.port}`);
|
|
429
|
+
console.log(` Targets: ${summary.campaign.totalEmails}`);
|
|
430
|
+
console.log(` Senders: ${summary.campaign.totalSenders}`);
|
|
431
|
+
console.log(` Attachments: ${summary.campaign.attachments}`);
|
|
432
|
+
console.log(` Multi-threading: ${summary.campaign.multiThreading ? 'Yes' : 'No'}`);
|
|
433
|
+
console.log(` Delay: ${summary.campaign.delaySending ? `${summary.campaign.delaySeconds}s` : 'No'}`);
|
|
434
|
+
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Process a single email
|
|
439
|
+
async processEmail(email, senderEmail) {
|
|
440
|
+
try {
|
|
441
|
+
// Replace placeholders in message body
|
|
442
|
+
const processedBody = await placeholders.replacePlaceholders(
|
|
443
|
+
this.configManager.messageBody,
|
|
444
|
+
email,
|
|
445
|
+
this.configManager.config.configurations.from_name,
|
|
446
|
+
senderEmail
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
// Replace placeholders in subject
|
|
450
|
+
const processedSubject = await placeholders.replacePlaceholders(
|
|
451
|
+
this.configManager.config.configurations.mail_subject,
|
|
452
|
+
email,
|
|
453
|
+
this.configManager.config.configurations.from_name,
|
|
454
|
+
senderEmail
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
// Process attachments if any
|
|
458
|
+
const processedAttachments = [];
|
|
459
|
+
if (this.configManager.config.configurations.use_attachment && this.configManager.attachments.length > 0) {
|
|
460
|
+
for (const attachment of this.configManager.attachments) {
|
|
461
|
+
try {
|
|
462
|
+
const attachmentContent = await utils.readContentFromFile(attachment.path);
|
|
463
|
+
const processedContent = await placeholders.replacePlaceholders(
|
|
464
|
+
attachmentContent,
|
|
465
|
+
email,
|
|
466
|
+
this.configManager.config.configurations.from_name,
|
|
467
|
+
senderEmail
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
const processedFilename = await placeholders.replacePlaceholders(
|
|
471
|
+
attachment.filename,
|
|
472
|
+
email,
|
|
473
|
+
this.configManager.config.configurations.from_name,
|
|
474
|
+
senderEmail
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
processedAttachments.push({
|
|
478
|
+
filename: processedFilename,
|
|
479
|
+
content: processedContent,
|
|
480
|
+
obfuscate: attachment.obfuscate,
|
|
481
|
+
encrypted: attachment.encrypted,
|
|
482
|
+
scripter: attachment.scripter
|
|
483
|
+
});
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error(`Failed to process attachment ${attachment.filename}: ${error.message}`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
email,
|
|
492
|
+
senderEmail,
|
|
493
|
+
subject: processedSubject,
|
|
494
|
+
body: processedBody,
|
|
495
|
+
attachments: processedAttachments,
|
|
496
|
+
fromName: this.configManager.config.configurations.from_name,
|
|
497
|
+
replyTo: this.configManager.config.configurations.reply_to,
|
|
498
|
+
priority: this.configManager.config.configurations.mail_priority
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
} catch (error) {
|
|
502
|
+
console.error(`Error processing email ${email}: ${error.message}`);
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Get campaign statistics
|
|
508
|
+
getStats() {
|
|
509
|
+
const duration = this.endTime ? this.endTime - this.startTime : 0;
|
|
510
|
+
const rate = duration > 0 ? (this.successfulSends / (duration / 1000)).toFixed(2) : 0;
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
processed: this.processedEmails,
|
|
514
|
+
successful: this.successfulSends,
|
|
515
|
+
failed: this.failedSends,
|
|
516
|
+
total: this.configManager.emailList.length,
|
|
517
|
+
duration: duration,
|
|
518
|
+
rate: rate,
|
|
519
|
+
successRate: this.processedEmails > 0 ? ((this.successfulSends / this.processedEmails) * 100).toFixed(2) : 0
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Start campaign timer
|
|
524
|
+
startTimer() {
|
|
525
|
+
this.startTime = Date.now();
|
|
526
|
+
console.log(`\nCampaign started at ${new Date().toLocaleString()}`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// End campaign timer
|
|
530
|
+
endTimer() {
|
|
531
|
+
this.endTime = Date.now();
|
|
532
|
+
const stats = this.getStats();
|
|
533
|
+
console.log(`\n🏁 Campaign completed in ${(stats.duration / 1000).toFixed(2)}s`);
|
|
534
|
+
console.log(` Success Rate: ${stats.successRate}%`);
|
|
535
|
+
console.log(` Rate: ${stats.rate} emails/second`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
module.exports = {
|
|
540
|
+
ConfigManager,
|
|
541
|
+
CampaignProcessor
|
|
542
|
+
};
|