@fanboynz/network-scanner 1.0.45 → 1.0.46

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.
@@ -0,0 +1,920 @@
1
+ /**
2
+ * Enhanced Mouse Interaction and Page Simulation Module
3
+ * ====================================================
4
+ *
5
+ * This module provides sophisticated, human-like interaction simulation for web scraping
6
+ * and automation tasks. It replaces basic mouse movements with realistic behavior patterns
7
+ * that are harder to detect by anti-bot systems.
8
+ *
9
+ * KEY FEATURES:
10
+ * - Human-like mouse movements with curves and jitter
11
+ * - Realistic scrolling simulation with smooth increments
12
+ * - Safe element interaction (avoids destructive actions)
13
+ * - Typing simulation with mistakes and variable timing
14
+ * - Configurable intensity levels (low/medium/high)
15
+ * - Site-specific optimization based on URL patterns
16
+ *
17
+ * USAGE EXAMPLES:
18
+ *
19
+ * Basic interaction:
20
+ * await performPageInteraction(page, url, {}, debug);
21
+ *
22
+ * Custom configuration:
23
+ * const config = createInteractionConfig(url, siteConfig);
24
+ * await performPageInteraction(page, url, config, debug);
25
+ *
26
+ * Manual mouse movement:
27
+ * await humanLikeMouseMove(page, 0, 0, 500, 300, {
28
+ * steps: 20, curve: 0.5, jitter: 3
29
+ * });
30
+ *
31
+ * CONFIGURATION OPTIONS:
32
+ * - intensity: 'low' | 'medium' | 'high' - Overall interaction intensity
33
+ * - duration: number - Total interaction time in milliseconds
34
+ * - mouseMovements: number - Number of mouse movements to perform
35
+ * - includeScrolling: boolean - Enable scrolling simulation
36
+ * - includeElementClicks: boolean - Enable safe element clicking
37
+ * - includeTyping: boolean - Enable typing simulation
38
+ *
39
+ * ANTI-DETECTION FEATURES:
40
+ * - Variable timing between actions
41
+ * - Curved mouse movements (not straight lines)
42
+ * - Random jitter and pauses
43
+ * - Site-specific behavior patterns
44
+ * - Realistic scrolling with momentum
45
+ * - Human-like typing with occasional mistakes
46
+ *
47
+ * SAFETY FEATURES:
48
+ * - Avoids clicking destructive elements (delete, buy, submit)
49
+ * - Bounded coordinate generation (stays within viewport)
50
+ * - Graceful error handling (failures don't break main scan)
51
+ * - Optional element interaction (disabled by default)
52
+ *
53
+ * @version 1.0
54
+ * @requires puppeteer
55
+ */
56
+
57
+ // === VIEWPORT AND COORDINATE CONSTANTS ===
58
+ // These control the default viewport assumptions and coordinate generation
59
+ const DEFAULT_VIEWPORT = {
60
+ WIDTH: 1200, // Default viewport width if not detected
61
+ HEIGHT: 800 // Default viewport height if not detected
62
+ };
63
+
64
+ const COORDINATE_MARGINS = {
65
+ DEFAULT_X: 50, // Minimum distance from left/right edges
66
+ DEFAULT_Y: 50, // Minimum distance from top/bottom edges
67
+ EDGE_ZONE_SIZE: 200, // Size of "edge" zones for preferEdges mode
68
+ CENTER_AVOID_RATIO: 0.25 // Percentage of viewport to avoid in center (0.25 = 25%)
69
+ };
70
+
71
+ // === MOUSE MOVEMENT CONSTANTS ===
72
+ // Fine-tune mouse movement behavior for realism vs. speed
73
+ const MOUSE_MOVEMENT = {
74
+ MIN_STEPS: 5, // Minimum steps for any movement
75
+ DEFAULT_STEPS: 15, // Default steps for mouse movement
76
+ MAX_STEPS: 30, // Maximum steps to prevent excessive slowness
77
+ MIN_DELAY: 5, // Minimum milliseconds between movement steps
78
+ MAX_DELAY: 25, // Maximum milliseconds between movement steps
79
+ DEFAULT_CURVE: 0.3, // Default curve intensity (0.0 = straight, 1.0 = very curved)
80
+ DEFAULT_JITTER: 2, // Default random jitter in pixels
81
+ DISTANCE_STEP_RATIO: 10, // Pixels per step (controls movement granularity)
82
+ CURVE_INTENSITY_RATIO: 0.01 // Multiplier for curve calculation
83
+ };
84
+
85
+ // === SCROLLING CONSTANTS ===
86
+ // Control scrolling behavior - adjust for different site types
87
+ const SCROLLING = {
88
+ DEFAULT_AMOUNT: 3, // Default number of scroll actions
89
+ DEFAULT_SMOOTHNESS: 5, // Default smoothness (higher = more increments)
90
+ SCROLL_DELTA: 200, // Pixels to scroll per action
91
+ PAUSE_BETWEEN: 200, // Milliseconds between scroll actions
92
+ SMOOTH_INCREMENT_DELAY: 20 // Milliseconds between smooth scroll increments
93
+ };
94
+
95
+ // === INTERACTION TIMING CONSTANTS ===
96
+ // All timing values in milliseconds - adjust for faster/slower interaction
97
+ const TIMING = {
98
+ CLICK_PAUSE_MIN: 100, // Minimum pause before clicking
99
+ CLICK_PAUSE_MAX: 200, // Maximum pause before clicking
100
+ POST_CLICK_MIN: 300, // Minimum pause after clicking
101
+ POST_CLICK_MAX: 500, // Maximum pause after clicking
102
+ TYPING_MIN_DELAY: 50, // Minimum delay between keystrokes
103
+ TYPING_MAX_DELAY: 150, // Maximum delay between keystrokes
104
+ MISTAKE_PAUSE_MIN: 100, // Minimum pause after typing mistake
105
+ MISTAKE_PAUSE_MAX: 200, // Maximum pause after typing mistake
106
+ BACKSPACE_DELAY_MIN: 50, // Minimum delay before backspace
107
+ BACKSPACE_DELAY_MAX: 100, // Maximum delay before backspace
108
+ DEFAULT_INTERACTION_DURATION: 2000 // Default total interaction time
109
+ };
110
+
111
+ // === ELEMENT INTERACTION CONSTANTS ===
112
+ // Safety and behavior settings for element interaction
113
+ const ELEMENT_INTERACTION = {
114
+ MAX_ATTEMPTS: 3, // Maximum attempts to find clickable elements
115
+ TIMEOUT: 2000, // Timeout for element operations
116
+ TEXT_PREVIEW_LENGTH: 50, // Characters to capture for element text preview
117
+ MISTAKE_RATE: 0.02 // Probability of typing mistakes (0.02 = 2% chance)
118
+ };
119
+
120
+ // === INTENSITY SETTINGS ===
121
+ // Pre-configured intensity levels - modify these to change overall behavior
122
+ const INTENSITY_SETTINGS = {
123
+ LOW: {
124
+ movements: 2, // Fewer movements for minimal interaction
125
+ scrolls: 1, // Minimal scrolling
126
+ pauseMultiplier: 1.5 // 50% longer pauses
127
+ },
128
+ MEDIUM: {
129
+ movements: 3, // Balanced movement count
130
+ scrolls: 2, // Moderate scrolling
131
+ pauseMultiplier: 1.0 // Normal timing
132
+ },
133
+ HIGH: {
134
+ movements: 5, // More movements for thorough interaction
135
+ scrolls: 3, // More scrolling activity
136
+ pauseMultiplier: 0.7 // 30% shorter pauses for faster interaction
137
+ }
138
+ };
139
+
140
+ // === SITE-SPECIFIC DURATION CONSTANTS ===
141
+ // Different interaction durations based on site type
142
+ const SITE_DURATIONS = {
143
+ NEWS_BLOG: 3000, // Longer duration for content-heavy sites
144
+ SOCIAL_FORUM: 2500, // Medium duration for social platforms
145
+ DEFAULT: 2000 // Standard duration for most sites
146
+ };
147
+
148
+ // === PROBABILITY CONSTANTS ===
149
+ // Control randomness and behavior patterns
150
+ const PROBABILITIES = {
151
+ PAUSE_CHANCE: 0.3, // 30% chance of random pause during movement
152
+ SCROLL_DOWN_BIAS: 0.7, // 70% chance to scroll down (vs up)
153
+ EDGE_PREFERENCE: { // Probabilities for edge selection in preferEdges mode
154
+ LEFT: 0.25, // 0-25% = left edge
155
+ RIGHT: 0.5, // 25-50% = right edge
156
+ TOP: 0.75, // 50-75% = top edge
157
+ BOTTOM: 1.0 // 75-100% = bottom edge
158
+ }
159
+ };
160
+
161
+ /**
162
+ * Generates random coordinates within viewport bounds with intelligent placement
163
+ *
164
+ * COORDINATE GENERATION MODES:
165
+ * - Normal: Random coordinates within margins
166
+ * - preferEdges: Bias towards viewport edges (more realistic)
167
+ * - avoidCenter: Exclude center area (useful for ads/popups)
168
+ *
169
+ * DEVELOPER NOTES:
170
+ * - Always respects marginX/marginY to prevent edge clipping
171
+ * - Edge zones are 200px from each edge by default
172
+ * - Center avoidance creates a circular exclusion zone
173
+ * - Returns {x, y} object with integer coordinates
174
+ *
175
+ * @param {number} maxX - Maximum X coordinate (viewport width)
176
+ * @param {number} maxY - Maximum Y coordinate (viewport height)
177
+ * @param {object} options - Configuration options
178
+ * @param {number} options.marginX - Minimum distance from left/right edges
179
+ * @param {number} options.marginY - Minimum distance from top/bottom edges
180
+ * @param {boolean} options.avoidCenter - Exclude center area (25% of viewport)
181
+ * @param {boolean} options.preferEdges - Bias coordinates towards edges
182
+ * @returns {object} Generated coordinates {x, y}
183
+ *
184
+ * @example
185
+ * // Basic random coordinates
186
+ * const pos = generateRandomCoordinates(1920, 1080);
187
+ *
188
+ * // Prefer edges for more natural movement
189
+ * const edgePos = generateRandomCoordinates(1920, 1080, { preferEdges: true });
190
+ *
191
+ * // Avoid center area (useful for avoiding ads)
192
+ * const safePos = generateRandomCoordinates(1920, 1080, { avoidCenter: true });
193
+ */
194
+ function generateRandomCoordinates(maxX = DEFAULT_VIEWPORT.WIDTH, maxY = DEFAULT_VIEWPORT.HEIGHT, options = {}) {
195
+ const {
196
+ marginX = COORDINATE_MARGINS.DEFAULT_X,
197
+ marginY = COORDINATE_MARGINS.DEFAULT_Y,
198
+ avoidCenter = false,
199
+ preferEdges = false
200
+ } = options;
201
+
202
+ let x, y;
203
+
204
+ if (preferEdges) {
205
+ // Prefer coordinates near edges for more realistic behavior
206
+ const edge = Math.random();
207
+ if (edge < PROBABILITIES.EDGE_PREFERENCE.LEFT) {
208
+ // Left edge
209
+ x = Math.floor(Math.random() * COORDINATE_MARGINS.EDGE_ZONE_SIZE) + marginX;
210
+ y = Math.floor(Math.random() * (maxY - 2 * marginY)) + marginY;
211
+ } else if (edge < PROBABILITIES.EDGE_PREFERENCE.RIGHT) {
212
+ // Right edge
213
+ x = Math.floor(Math.random() * COORDINATE_MARGINS.EDGE_ZONE_SIZE) + (maxX - COORDINATE_MARGINS.EDGE_ZONE_SIZE - marginX);
214
+ y = Math.floor(Math.random() * (maxY - 2 * marginY)) + marginY;
215
+ } else if (edge < PROBABILITIES.EDGE_PREFERENCE.TOP) {
216
+ // Top edge
217
+ x = Math.floor(Math.random() * (maxX - 2 * marginX)) + marginX;
218
+ y = Math.floor(Math.random() * COORDINATE_MARGINS.EDGE_ZONE_SIZE) + marginY;
219
+ } else {
220
+ // Bottom edge
221
+ x = Math.floor(Math.random() * (maxX - 2 * marginX)) + marginX;
222
+ y = Math.floor(Math.random() * COORDINATE_MARGINS.EDGE_ZONE_SIZE) + (maxY - COORDINATE_MARGINS.EDGE_ZONE_SIZE - marginY);
223
+ }
224
+ } else if (avoidCenter) {
225
+ // Avoid center area
226
+ const centerX = maxX / 2;
227
+ const centerY = maxY / 2;
228
+ const avoidRadius = Math.min(maxX, maxY) * COORDINATE_MARGINS.CENTER_AVOID_RATIO;
229
+
230
+ do {
231
+ x = Math.floor(Math.random() * (maxX - 2 * marginX)) + marginX;
232
+ y = Math.floor(Math.random() * (maxY - 2 * marginY)) + marginY;
233
+ } while (Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2) < avoidRadius);
234
+ } else {
235
+ // Standard random coordinates
236
+ x = Math.floor(Math.random() * (maxX - 2 * marginX)) + marginX;
237
+ y = Math.floor(Math.random() * (maxY - 2 * marginY)) + marginY;
238
+ }
239
+
240
+ return { x, y };
241
+ }
242
+
243
+ /**
244
+ * Simulates human-like mouse movement with realistic timing and curves
245
+ *
246
+ * MOVEMENT CHARACTERISTICS:
247
+ * - Uses easing curves (slow start, fast middle, slow end)
248
+ * - Adds slight curve to path (not straight lines)
249
+ * - Random jitter for micro-movements
250
+ * - Variable timing between steps
251
+ * - Automatically calculates optimal step count based on distance
252
+ *
253
+ * PERFORMANCE NOTES:
254
+ * - Longer distances automatically use more steps
255
+ * - Very short movements use minimum steps to prevent slowness
256
+ * - Maximum steps cap prevents excessive delays
257
+ *
258
+ * ANTI-DETECTION FEATURES:
259
+ * - No perfectly straight lines
260
+ * - Realistic acceleration/deceleration
261
+ * - Micro-movements simulate hand tremor
262
+ * - Variable timing prevents pattern detection
263
+ *
264
+ * @param {import('puppeteer').Page} page - Puppeteer page object
265
+ * @param {number} fromX - Starting X coordinate
266
+ * @param {number} fromY - Starting Y coordinate
267
+ * @param {number} toX - Target X coordinate
268
+ * @param {number} toY - Target Y coordinate
269
+ * @param {object} options - Movement configuration
270
+ * @param {number} options.steps - Number of movement steps (auto-calculated if not specified)
271
+ * @param {number} options.minDelay - Minimum delay between steps in ms
272
+ * @param {number} options.maxDelay - Maximum delay between steps in ms
273
+ * @param {number} options.curve - Curve intensity (0.0 = straight, 1.0 = very curved)
274
+ * @param {number} options.jitter - Random jitter amount in pixels
275
+ *
276
+ * @example
277
+ * // Basic movement
278
+ * await humanLikeMouseMove(page, 0, 0, 500, 300);
279
+ *
280
+ * // Slow, very curved movement
281
+ * await humanLikeMouseMove(page, 0, 0, 500, 300, {
282
+ * steps: 25, curve: 0.8, minDelay: 20, maxDelay: 50
283
+ * });
284
+ *
285
+ * // Fast, minimal curve movement
286
+ * await humanLikeMouseMove(page, 0, 0, 500, 300, {
287
+ * steps: 8, curve: 0.1, minDelay: 2, maxDelay: 8
288
+ * });
289
+ */
290
+ async function humanLikeMouseMove(page, fromX, fromY, toX, toY, options = {}) {
291
+ const {
292
+ steps = MOUSE_MOVEMENT.DEFAULT_STEPS,
293
+ minDelay = MOUSE_MOVEMENT.MIN_DELAY,
294
+ maxDelay = MOUSE_MOVEMENT.MAX_DELAY,
295
+ curve = MOUSE_MOVEMENT.DEFAULT_CURVE,
296
+ jitter = MOUSE_MOVEMENT.DEFAULT_JITTER
297
+ } = options;
298
+
299
+ const distance = Math.sqrt((toX - fromX) ** 2 + (toY - fromY) ** 2);
300
+ const actualSteps = Math.max(
301
+ MOUSE_MOVEMENT.MIN_STEPS,
302
+ Math.min(steps, Math.floor(distance / MOUSE_MOVEMENT.DISTANCE_STEP_RATIO))
303
+ );
304
+
305
+ for (let i = 0; i <= actualSteps; i++) {
306
+ const progress = i / actualSteps;
307
+
308
+ // Apply easing curve for more natural movement
309
+ const easedProgress = progress < 0.5
310
+ ? 2 * progress * progress
311
+ : 1 - Math.pow(-2 * progress + 2, 2) / 2;
312
+
313
+ // Calculate base position
314
+ let currentX = fromX + (toX - fromX) * easedProgress;
315
+ let currentY = fromY + (toY - fromY) * easedProgress;
316
+
317
+ // Add slight curve to movement (more human-like)
318
+ if (curve > 0 && i > 0 && i < actualSteps) {
319
+ const midpoint = actualSteps / 2;
320
+ const curveIntensity = Math.sin((i / actualSteps) * Math.PI) * curve * distance * MOUSE_MOVEMENT.CURVE_INTENSITY_RATIO;
321
+ const perpX = -(toY - fromY) / distance;
322
+ const perpY = (toX - fromX) / distance;
323
+
324
+ currentX += perpX * curveIntensity;
325
+ currentY += perpY * curveIntensity;
326
+ }
327
+
328
+ // Add small random jitter for realism
329
+ if (jitter > 0 && i > 0 && i < actualSteps) {
330
+ currentX += (Math.random() - 0.5) * jitter;
331
+ currentY += (Math.random() - 0.5) * jitter;
332
+ }
333
+
334
+ await page.mouse.move(currentX, currentY);
335
+
336
+ // Variable delay between movements
337
+ if (i < actualSteps) {
338
+ const delay = Math.floor(Math.random() * (maxDelay - minDelay + 1)) + minDelay;
339
+ await new Promise(resolve => setTimeout(resolve, delay));
340
+ }
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Simulates realistic scrolling behavior with momentum and smoothness
346
+ *
347
+ * SCROLLING FEATURES:
348
+ * - Smooth scrolling broken into increments (not instant jumps)
349
+ * - Configurable direction (up/down)
350
+ * - Variable scroll amounts and speeds
351
+ * - Pauses between scroll actions for realism
352
+ *
353
+ * DEVELOPER NOTES:
354
+ * - Uses page.mouse.wheel() for browser-native scrolling
355
+ * - Smoothness parameter controls increment count (higher = smoother)
356
+ * - Each scroll action is split into multiple small increments
357
+ * - Automatically handles scroll failures gracefully
358
+ *
359
+ * @param {import('puppeteer').Page} page - Puppeteer page object
360
+ * @param {object} options - Scrolling configuration
361
+ * @param {string} options.direction - 'down' or 'up'
362
+ * @param {number} options.amount - Number of scroll actions to perform
363
+ * @param {number} options.smoothness - Smoothness level (1-10, higher = smoother)
364
+ * @param {number} options.pauseBetween - Milliseconds pause between scroll actions
365
+ *
366
+ * @example
367
+ * // Basic downward scrolling
368
+ * await simulateScrolling(page);
369
+ *
370
+ * // Smooth upward scrolling
371
+ * await simulateScrolling(page, {
372
+ * direction: 'up', amount: 5, smoothness: 8
373
+ * });
374
+ *
375
+ * // Fast scrolling with minimal smoothness
376
+ * await simulateScrolling(page, {
377
+ * direction: 'down', amount: 2, smoothness: 2, pauseBetween: 100
378
+ * });
379
+ */
380
+ async function simulateScrolling(page, options = {}) {
381
+ const {
382
+ direction = 'down',
383
+ amount = SCROLLING.DEFAULT_AMOUNT,
384
+ smoothness = SCROLLING.DEFAULT_SMOOTHNESS,
385
+ pauseBetween = SCROLLING.PAUSE_BETWEEN
386
+ } = options;
387
+
388
+ try {
389
+ for (let i = 0; i < amount; i++) {
390
+ const scrollDelta = direction === 'down' ? SCROLLING.SCROLL_DELTA : -SCROLLING.SCROLL_DELTA;
391
+
392
+ // Smooth scrolling by breaking into smaller increments
393
+ for (let j = 0; j < smoothness; j++) {
394
+ await page.mouse.wheel({ deltaY: scrollDelta / smoothness });
395
+ await new Promise(resolve => setTimeout(resolve, SCROLLING.SMOOTH_INCREMENT_DELAY));
396
+ }
397
+
398
+ if (i < amount - 1) {
399
+ await new Promise(resolve => setTimeout(resolve, pauseBetween));
400
+ }
401
+ }
402
+ } catch (scrollErr) {
403
+ // Silently handle scroll errors - not critical for functionality
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Attempts to find and interact with clickable elements safely
409
+ *
410
+ * SAFETY FEATURES:
411
+ * - Avoids destructive actions (delete, buy, submit buttons)
412
+ * - Only interacts with visible, clickable elements
413
+ * - Bounded to viewport coordinates
414
+ * - Graceful failure handling
415
+ *
416
+ * ELEMENT DETECTION:
417
+ * - Searches for buttons, links, and role="button" elements
418
+ * - Filters by visibility (width/height > 0, within viewport)
419
+ * - Text-based filtering to avoid dangerous actions
420
+ * - Random selection from available safe elements
421
+ *
422
+ * INTERACTION FLOW:
423
+ * 1. Find all matching elements in viewport
424
+ * 2. Filter out dangerous elements by text content
425
+ * 3. Randomly select one element
426
+ * 4. Move mouse to element center
427
+ * 5. Pause briefly, then click
428
+ * 6. Pause after clicking
429
+ *
430
+ * DEVELOPER NOTES:
431
+ * - Set avoidDestructive: false to disable safety filtering
432
+ * - Customize elementTypes to target specific element types
433
+ * - maxAttempts controls retry behavior
434
+ * - All errors are caught to prevent breaking main scan
435
+ *
436
+ * @param {import('puppeteer').Page} page - Puppeteer page object
437
+ * @param {object} options - Element interaction configuration
438
+ * @param {number} options.maxAttempts - Maximum attempts to find elements
439
+ * @param {string[]} options.elementTypes - CSS selectors for clickable elements
440
+ * @param {boolean} options.avoidDestructive - Avoid dangerous actions
441
+ * @param {number} options.timeout - Timeout for element operations
442
+ *
443
+ * @example
444
+ * // Safe element interaction (default)
445
+ * await interactWithElements(page);
446
+ *
447
+ * // Custom element types
448
+ * await interactWithElements(page, {
449
+ * elementTypes: ['button', '.custom-button', '#specific-id'],
450
+ * maxAttempts: 5
451
+ * });
452
+ *
453
+ * // Allow all interactions (dangerous!)
454
+ * await interactWithElements(page, {
455
+ * avoidDestructive: false,
456
+ * elementTypes: ['button', 'input[type="submit"]']
457
+ * });
458
+ */
459
+ async function interactWithElements(page, options = {}) {
460
+ const {
461
+ maxAttempts = ELEMENT_INTERACTION.MAX_ATTEMPTS,
462
+ elementTypes = ['button', 'a', '[role="button"]'],
463
+ avoidDestructive = true,
464
+ timeout = ELEMENT_INTERACTION.TIMEOUT
465
+ } = options;
466
+
467
+ try {
468
+ // Get viewport dimensions for coordinate bounds
469
+ const viewport = await page.viewport();
470
+ const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
471
+ const maxY = viewport ? viewport.height : DEFAULT_VIEWPORT.HEIGHT;
472
+
473
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
474
+ try {
475
+ // Find visible, clickable elements
476
+ const elements = await page.evaluate((selectors, avoidWords) => {
477
+ const clickableElements = [];
478
+
479
+ selectors.forEach(selector => {
480
+ const elements = document.querySelectorAll(selector);
481
+ elements.forEach(el => {
482
+ const rect = el.getBoundingClientRect();
483
+ const isVisible = rect.width > 0 && rect.height > 0 &&
484
+ rect.top >= 0 && rect.left >= 0 &&
485
+ rect.bottom <= window.innerHeight &&
486
+ rect.right <= window.innerWidth;
487
+
488
+ if (isVisible) {
489
+ const text = (el.textContent || el.alt || el.title || '').toLowerCase();
490
+ const shouldAvoid = avoidWords && avoidWords.some(word => text.includes(word));
491
+
492
+ if (!shouldAvoid) {
493
+ clickableElements.push({
494
+ x: rect.left + rect.width / 2,
495
+ y: rect.top + rect.height / 2,
496
+ width: rect.width,
497
+ height: rect.height,
498
+ text: text.substring(0, ELEMENT_INTERACTION.TEXT_PREVIEW_LENGTH)
499
+ });
500
+ }
501
+ }
502
+ });
503
+ });
504
+
505
+ return clickableElements;
506
+ }, elementTypes, avoidDestructive ? ['delete', 'remove', 'submit', 'buy', 'purchase', 'order'] : []);
507
+
508
+ if (elements.length > 0) {
509
+ // Choose a random element to interact with
510
+ const element = elements[Math.floor(Math.random() * elements.length)];
511
+
512
+ // Move to element and click
513
+ const currentPos = generateRandomCoordinates(maxX, maxY);
514
+ await humanLikeMouseMove(page, currentPos.x, currentPos.y, element.x, element.y);
515
+
516
+ // Brief pause before clicking
517
+ await new Promise(resolve => setTimeout(resolve, TIMING.CLICK_PAUSE_MIN + Math.random() * TIMING.CLICK_PAUSE_MAX));
518
+
519
+ await page.mouse.click(element.x, element.y);
520
+
521
+ // Brief pause after clicking
522
+ await new Promise(resolve => setTimeout(resolve, TIMING.POST_CLICK_MIN + Math.random() * TIMING.POST_CLICK_MAX));
523
+ }
524
+ } catch (elementErr) {
525
+ // Continue to next attempt if this one fails
526
+ continue;
527
+ }
528
+ }
529
+ } catch (mainErr) {
530
+ // Silently handle errors - element interaction is supplementary
531
+ }
532
+ }
533
+
534
+ /**
535
+ * Simulates realistic typing behavior with human characteristics
536
+ *
537
+ * TYPING CHARACTERISTICS:
538
+ * - Variable delay between keystrokes
539
+ * - Optional typing mistakes with correction
540
+ * - Realistic backspace timing
541
+ * - Character-by-character typing (not paste)
542
+ *
543
+ * MISTAKE SIMULATION:
544
+ * - Random wrong characters (2% default rate)
545
+ * - Pause after mistake (human realization delay)
546
+ * - Backspace to correct
547
+ * - Continue with correct character
548
+ *
549
+ * DEVELOPER NOTES:
550
+ * - Requires an active input field with focus
551
+ * - All typing errors are silently handled
552
+ * - Mistake rate should be low (0.01-0.05) for realism
553
+ * - Use for form filling or search simulation
554
+ *
555
+ * @param {import('puppeteer').Page} page - Puppeteer page object
556
+ * @param {string} text - Text to type
557
+ * @param {object} options - Typing configuration
558
+ * @param {number} options.minDelay - Minimum delay between keystrokes
559
+ * @param {number} options.maxDelay - Maximum delay between keystrokes
560
+ * @param {boolean} options.mistakes - Enable typing mistakes
561
+ * @param {number} options.mistakeRate - Probability of mistakes (0.0-1.0)
562
+ *
563
+ * @example
564
+ * // Basic typing
565
+ * await simulateTyping(page, "hello world");
566
+ *
567
+ * // Slow typing with mistakes
568
+ * await simulateTyping(page, "search query", {
569
+ * minDelay: 100, maxDelay: 300, mistakes: true, mistakeRate: 0.03
570
+ * });
571
+ *
572
+ * // Fast typing without mistakes
573
+ * await simulateTyping(page, "username", {
574
+ * minDelay: 30, maxDelay: 80, mistakes: false
575
+ * });
576
+ */
577
+ async function simulateTyping(page, text, options = {}) {
578
+ const {
579
+ minDelay = TIMING.TYPING_MIN_DELAY,
580
+ maxDelay = TIMING.TYPING_MAX_DELAY,
581
+ mistakes = false,
582
+ mistakeRate = ELEMENT_INTERACTION.MISTAKE_RATE
583
+ } = options;
584
+
585
+ try {
586
+ for (let i = 0; i < text.length; i++) {
587
+ const char = text[i];
588
+
589
+ // Simulate occasional typing mistakes
590
+ if (mistakes && Math.random() < mistakeRate) {
591
+ const wrongChar = String.fromCharCode(97 + Math.floor(Math.random() * 26));
592
+ await page.keyboard.type(wrongChar);
593
+ await new Promise(resolve => setTimeout(resolve, TIMING.MISTAKE_PAUSE_MIN + Math.random() * TIMING.MISTAKE_PAUSE_MAX));
594
+ await page.keyboard.press('Backspace');
595
+ await new Promise(resolve => setTimeout(resolve, TIMING.BACKSPACE_DELAY_MIN + Math.random() * TIMING.BACKSPACE_DELAY_MAX));
596
+ }
597
+
598
+ await page.keyboard.type(char);
599
+
600
+ // Variable delay between keystrokes
601
+ const delay = Math.floor(Math.random() * (maxDelay - minDelay + 1)) + minDelay;
602
+ await new Promise(resolve => setTimeout(resolve, delay));
603
+ }
604
+ } catch (typingErr) {
605
+ // Silently handle typing errors
606
+ }
607
+ }
608
+
609
+ /**
610
+ * Performs comprehensive page interaction simulation - MAIN ENTRY POINT
611
+ *
612
+ * This is the primary function called by nwss.js for page interaction.
613
+ * It orchestrates multiple interaction types based on configuration.
614
+ *
615
+ * INTERACTION SEQUENCE:
616
+ * 1. Move mouse to random starting position
617
+ * 2. Perform configured number of mouse movements
618
+ * 3. Add occasional pauses for realism
619
+ * 4. Simulate scrolling (if enabled)
620
+ * 5. Interact with elements (if enabled)
621
+ * 6. End with final hover position
622
+ *
623
+ * INTENSITY LEVELS:
624
+ * - LOW: 2 movements, 1 scroll, 50% longer pauses
625
+ * - MEDIUM: 3 movements, 2 scrolls, normal timing
626
+ * - HIGH: 5 movements, 3 scrolls, 30% faster timing
627
+ *
628
+ * SAFETY FEATURES:
629
+ * - All errors are caught and logged (won't break main scan)
630
+ * - Element clicking is disabled by default
631
+ * - Destructive actions are avoided
632
+ * - Respects viewport boundaries
633
+ *
634
+ * PERFORMANCE NOTES:
635
+ * - Duration is distributed across all actions
636
+ * - Actions are time-spaced for even distribution
637
+ * - Intensity affects both quantity and timing
638
+ *
639
+ * @param {import('puppeteer').Page} page - Puppeteer page object
640
+ * @param {string} currentUrl - Current page URL for logging
641
+ * @param {object} options - Interaction configuration
642
+ * @param {number} options.mouseMovements - Number of mouse movements
643
+ * @param {boolean} options.includeScrolling - Enable scrolling simulation
644
+ * @param {boolean} options.includeElementClicks - Enable element clicking
645
+ * @param {boolean} options.includeTyping - Enable typing simulation
646
+ * @param {number} options.duration - Total interaction time in milliseconds
647
+ * @param {string} options.intensity - 'low' | 'medium' | 'high'
648
+ * @param {boolean} forceDebug - Enable debug logging
649
+ *
650
+ * @example
651
+ * // Basic interaction
652
+ * await performPageInteraction(page, 'https://example.com');
653
+ *
654
+ * // High intensity interaction
655
+ * await performPageInteraction(page, 'https://news.com', {
656
+ * intensity: 'high',
657
+ * duration: 5000,
658
+ * includeScrolling: true
659
+ * });
660
+ *
661
+ * // Minimal interaction
662
+ * await performPageInteraction(page, 'https://shop.com', {
663
+ * intensity: 'low',
664
+ * mouseMovements: 1,
665
+ * includeScrolling: false,
666
+ * includeElementClicks: false
667
+ * });
668
+ */
669
+ async function performPageInteraction(page, currentUrl, options = {}, forceDebug = false) {
670
+ const {
671
+ mouseMovements = INTENSITY_SETTINGS.MEDIUM.movements,
672
+ includeScrolling = true,
673
+ includeElementClicks = false,
674
+ includeTyping = false,
675
+ duration = TIMING.DEFAULT_INTERACTION_DURATION,
676
+ intensity = 'medium'
677
+ } = options;
678
+
679
+ try {
680
+ // Get viewport dimensions
681
+ const viewport = await page.viewport();
682
+ const maxX = viewport ? viewport.width : DEFAULT_VIEWPORT.WIDTH;
683
+ const maxY = viewport ? viewport.height : DEFAULT_VIEWPORT.HEIGHT;
684
+
685
+ if (forceDebug) {
686
+ console.log(`[interaction] Starting enhanced interaction simulation for ${new URL(currentUrl).hostname} (${intensity} intensity)`);
687
+ }
688
+
689
+ // Configure intensity settings
690
+ const settings = INTENSITY_SETTINGS[intensity.toUpperCase()] || INTENSITY_SETTINGS.MEDIUM;
691
+ const actualMovements = Math.min(mouseMovements, settings.movements);
692
+
693
+ // Start with random position
694
+ let currentPos = generateRandomCoordinates(maxX, maxY, { preferEdges: true });
695
+ await page.mouse.move(currentPos.x, currentPos.y);
696
+
697
+ const startTime = Date.now();
698
+ const totalDuration = duration * settings.pauseMultiplier;
699
+ const actionInterval = totalDuration / (actualMovements + (includeScrolling ? settings.scrolls : 0));
700
+
701
+ // Perform mouse movements
702
+ for (let i = 0; i < actualMovements; i++) {
703
+ const targetPos = generateRandomCoordinates(maxX, maxY, {
704
+ avoidCenter: i % 2 === 0,
705
+ preferEdges: i % 3 === 0
706
+ });
707
+
708
+ await humanLikeMouseMove(page, currentPos.x, currentPos.y, targetPos.x, targetPos.y, {
709
+ steps: 10 + Math.floor(Math.random() * 15),
710
+ curve: 0.2 + Math.random() * 0.3,
711
+ jitter: 1 + Math.random() * 2
712
+ });
713
+
714
+ currentPos = targetPos;
715
+
716
+ // Occasional pause
717
+ if (Math.random() < PROBABILITIES.PAUSE_CHANCE) {
718
+ await new Promise(resolve => setTimeout(resolve, TIMING.CLICK_PAUSE_MIN + Math.random() * TIMING.POST_CLICK_MIN));
719
+ }
720
+
721
+ // Time-based spacing
722
+ await new Promise(resolve => setTimeout(resolve, actionInterval));
723
+ }
724
+
725
+ // Scrolling simulation
726
+ if (includeScrolling) {
727
+ for (let i = 0; i < settings.scrolls; i++) {
728
+ const direction = Math.random() < PROBABILITIES.SCROLL_DOWN_BIAS ? 'down' : 'up';
729
+ await simulateScrolling(page, {
730
+ direction,
731
+ amount: 2 + Math.floor(Math.random() * 3),
732
+ smoothness: 3 + Math.floor(Math.random() * 4)
733
+ });
734
+
735
+ await new Promise(resolve => setTimeout(resolve, actionInterval));
736
+ }
737
+ }
738
+
739
+ // Element interaction
740
+ if (includeElementClicks) {
741
+ await interactWithElements(page, {
742
+ maxAttempts: 2,
743
+ avoidDestructive: true
744
+ });
745
+ }
746
+
747
+ // Final hover position
748
+ const finalPos = generateRandomCoordinates(maxX, maxY);
749
+ await humanLikeMouseMove(page, currentPos.x, currentPos.y, finalPos.x, finalPos.y);
750
+ await page.hover('body');
751
+
752
+ const elapsedTime = Date.now() - startTime;
753
+ if (forceDebug) {
754
+ console.log(`[interaction] Completed interaction simulation in ${elapsedTime}ms (${actualMovements} movements, ${includeScrolling ? settings.scrolls : 0} scrolls)`);
755
+ }
756
+
757
+ } catch (interactionErr) {
758
+ if (forceDebug) {
759
+ console.log(`[interaction] Interaction simulation failed for ${currentUrl}: ${interactionErr.message}`);
760
+ }
761
+ // Don't throw - interaction failures shouldn't break the main scan
762
+ }
763
+ }
764
+
765
+ /**
766
+ * Creates an optimized interaction configuration based on site characteristics
767
+ *
768
+ * This function analyzes the target URL and creates an appropriate interaction
769
+ * configuration automatically. It can be overridden by explicit site config.
770
+ *
771
+ * AUTOMATIC SITE DETECTION:
772
+ * - News/Blog sites: High intensity, longer duration, more scrolling
773
+ * - Shopping sites: Low intensity, avoid clicking (safety)
774
+ * - Social/Forum sites: Medium intensity, balanced interaction
775
+ * - Default: Medium intensity for unknown sites
776
+ *
777
+ * CONFIGURATION PRIORITY:
778
+ * 1. Explicit siteConfig parameters (highest priority)
779
+ * 2. URL-based automatic detection
780
+ * 3. Default values (lowest priority)
781
+ *
782
+ * SITE CONFIG OVERRIDES:
783
+ * - interact_intensity: 'low' | 'medium' | 'high'
784
+ * - interact_duration: milliseconds
785
+ * - interact_scrolling: boolean
786
+ * - interact_clicks: boolean
787
+ * - interact_typing: boolean
788
+ *
789
+ * DEVELOPER NOTES:
790
+ * - Add new site patterns by modifying the hostname checks
791
+ * - Site detection is case-insensitive substring matching
792
+ * - Returns a complete config object with all required properties
793
+ * - Gracefully handles malformed URLs
794
+ *
795
+ * @param {string} url - Site URL for analysis
796
+ * @param {object} siteConfig - Site-specific configuration overrides
797
+ * @returns {object} Optimized interaction configuration
798
+ *
799
+ * @example
800
+ * // Automatic configuration
801
+ * const config = createInteractionConfig('https://news.example.com');
802
+ * // Returns: { intensity: 'high', duration: 3000, includeScrolling: true, ... }
803
+ *
804
+ * // With manual overrides
805
+ * const config = createInteractionConfig('https://shop.com', {
806
+ * interact_intensity: 'medium',
807
+ * interact_clicks: true
808
+ * });
809
+ * // Returns: { intensity: 'medium', includeElementClicks: true, ... }
810
+ *
811
+ * // Custom site pattern
812
+ * const config = createInteractionConfig('https://custom-forum.com');
813
+ * // Falls back to default configuration
814
+ */
815
+ function createInteractionConfig(url, siteConfig = {}) {
816
+ try {
817
+ const hostname = new URL(url).hostname.toLowerCase();
818
+
819
+ // Site-specific interaction patterns
820
+ const config = {
821
+ mouseMovements: 3,
822
+ includeScrolling: true,
823
+ includeElementClicks: false,
824
+ includeTyping: false,
825
+ duration: 2000,
826
+ intensity: 'medium'
827
+ };
828
+
829
+ // Adjust based on site type
830
+ if (hostname.includes('news') || hostname.includes('blog')) {
831
+ config.includeScrolling = true;
832
+ config.intensity = 'high';
833
+ config.duration = SITE_DURATIONS.NEWS_BLOG;
834
+ } else if (hostname.includes('shop') || hostname.includes('store')) {
835
+ config.includeElementClicks = false; // Avoid accidental purchases
836
+ config.intensity = 'low';
837
+ } else if (hostname.includes('social') || hostname.includes('forum')) {
838
+ config.includeScrolling = true;
839
+ config.mouseMovements = 4;
840
+ config.intensity = 'medium';
841
+ config.duration = SITE_DURATIONS.SOCIAL_FORUM;
842
+ }
843
+
844
+ // Override with explicit site configuration
845
+ if (siteConfig.interact_intensity) {
846
+ config.intensity = siteConfig.interact_intensity;
847
+ }
848
+ if (siteConfig.interact_duration) {
849
+ config.duration = siteConfig.interact_duration;
850
+ }
851
+ if (siteConfig.interact_scrolling !== undefined) {
852
+ config.includeScrolling = siteConfig.interact_scrolling;
853
+ }
854
+ if (siteConfig.interact_clicks !== undefined) {
855
+ config.includeElementClicks = siteConfig.interact_clicks;
856
+ }
857
+
858
+ return config;
859
+ } catch (urlErr) {
860
+ // Return default config if URL parsing fails
861
+ return {
862
+ mouseMovements: INTENSITY_SETTINGS.MEDIUM.movements,
863
+ includeScrolling: true,
864
+ includeElementClicks: false,
865
+ includeTyping: false,
866
+ duration: TIMING.DEFAULT_INTERACTION_DURATION,
867
+ intensity: 'medium'
868
+ };
869
+ }
870
+ }
871
+
872
+ // === MODULE EXPORTS ===
873
+ // Export all public functions for use by nwss.js and other modules
874
+
875
+ /**
876
+ * MAIN EXPORTS - Primary functions for page interaction
877
+ *
878
+ * performPageInteraction: Main entry point for comprehensive interaction
879
+ * createInteractionConfig: Auto-generates optimized config based on URL
880
+ */
881
+
882
+ /**
883
+ * COMPONENT EXPORTS - Individual interaction components
884
+ *
885
+ * humanLikeMouseMove: Realistic mouse movement with curves
886
+ * simulateScrolling: Smooth scrolling simulation
887
+ * interactWithElements: Safe element clicking
888
+ * simulateTyping: Human-like typing with mistakes
889
+ * generateRandomCoordinates: Smart coordinate generation
890
+ */
891
+
892
+ /**
893
+ * USAGE EXAMPLES:
894
+ *
895
+ * // In nwss.js (main integration)
896
+ * const { performPageInteraction, createInteractionConfig } = require('./lib/interaction');
897
+ * const config = createInteractionConfig(url, siteConfig);
898
+ * await performPageInteraction(page, url, config, debug);
899
+ *
900
+ * // Custom interaction script
901
+ * const { humanLikeMouseMove, simulateScrolling } = require('./lib/interaction');
902
+ * await humanLikeMouseMove(page, 0, 0, 500, 300);
903
+ * await simulateScrolling(page, { direction: 'down', amount: 3 });
904
+ *
905
+ * // Advanced coordinate generation
906
+ * const { generateRandomCoordinates } = require('./lib/interaction');
907
+ * const pos = generateRandomCoordinates(1920, 1080, { preferEdges: true });
908
+ */
909
+ module.exports = {
910
+ // Main interaction functions
911
+ performPageInteraction,
912
+ createInteractionConfig,
913
+
914
+ // Component functions for custom implementations
915
+ humanLikeMouseMove,
916
+ simulateScrolling,
917
+ interactWithElements,
918
+ simulateTyping,
919
+ generateRandomCoordinates
920
+ };
package/nwss.js CHANGED
@@ -1,4 +1,4 @@
1
- // === Network scanner script (nwss.js) v1.0.45 ===
1
+ // === Network scanner script (nwss.js) v1.0.46 ===
2
2
 
3
3
  // puppeteer for browser automation, fs for file system operations, psl for domain parsing.
4
4
  // const pLimit = require('p-limit'); // Will be dynamically imported
@@ -27,6 +27,8 @@ const { createNetToolsHandler, createEnhancedDryRunCallback, validateWhoisAvaila
27
27
  const { loadComparisonRules, filterUniqueRules } = require('./lib/compare');
28
28
  // Colorize various text when used
29
29
  const { colorize, colors, messageColors, tags, formatLogMessage } = require('./lib/colorize');
30
+ // Enhanced mouse interaction and page simulation
31
+ const { performPageInteraction, createInteractionConfig } = require('./lib/interaction');
30
32
  // Domain detection cache for performance optimization
31
33
  const { createGlobalHelpers, getTotalDomainsSkipped, getDetectedDomainsCount } = require('./lib/domain-cache');
32
34
  // Enhanced redirect handling
@@ -35,7 +37,7 @@ const { navigateWithRedirectHandling, handleRedirectTimeout } = require('./lib/r
35
37
  const { monitorBrowserHealth, isBrowserHealthy } = require('./lib/browserhealth');
36
38
 
37
39
  // --- Script Configuration & Constants ---
38
- const VERSION = '1.0.45'; // Script version
40
+ const VERSION = '1.0.46'; // Script version
39
41
 
40
42
  // get startTime
41
43
  const startTime = Date.now();
@@ -390,6 +392,7 @@ Redirect Handling Options:
390
392
  interact: true/false Simulate mouse movements/clicks
391
393
  isBrave: true/false Spoof Brave browser detection
392
394
  userAgent: "chrome"|"firefox"|"safari" Custom desktop User-Agent
395
+ interact_intensity: "low"|"medium"|"high" Interaction simulation intensity (default: medium)
393
396
  delay: <milliseconds> Delay after load (default: 4000)
394
397
  reload: <number> Reload page n times after load (default: 1)
395
398
  forcereload: true/false Force an additional reload after reloads
@@ -429,6 +432,10 @@ FlowProxy Protection Options:
429
432
  Advanced Options:
430
433
  evaluateOnNewDocument: true/false Inject fetch/XHR interceptor in page (for this site)
431
434
  cdp: true/false Enable CDP logging for this site Inject fetch/XHR interceptor in page
435
+ interact_duration: <milliseconds> Duration of interaction simulation (default: 2000)
436
+ interact_scrolling: true/false Enable scrolling simulation (default: true)
437
+ interact_clicks: true/false Enable element clicking simulation (default: false)
438
+ interact_typing: true/false Enable typing simulation (default: false)
432
439
  whois: ["term1", "term2"] Check whois data for ALL specified terms (AND logic)
433
440
  whois-or: ["term1", "term2"] Check whois data for ANY specified term (OR logic)
434
441
  whois_server_mode: "random" or "cycle" Server selection mode: random (default) or cycle through list
@@ -1927,6 +1934,9 @@ function setupFrameHandling(page, forceDebug) {
1927
1934
 
1928
1935
  const interactEnabled = siteConfig.interact === true;
1929
1936
 
1937
+ // Create optimized interaction configuration for this site
1938
+ const interactionConfig = createInteractionConfig(currentUrl, siteConfig);
1939
+
1930
1940
  // --- Runtime CSS Element Blocking (Fallback) ---
1931
1941
  // Apply CSS blocking after page load as a fallback in case evaluateOnNewDocument didn't work
1932
1942
  if (cssBlockedSelectors && Array.isArray(cssBlockedSelectors) && cssBlockedSelectors.length > 0) {
@@ -2080,12 +2090,8 @@ function setupFrameHandling(page, forceDebug) {
2080
2090
 
2081
2091
  if (interactEnabled && !disableInteract) {
2082
2092
  if (forceDebug) console.log(formatLogMessage('debug', `interaction simulation enabled for ${currentUrl}`));
2083
- const randomX = Math.floor(Math.random() * 500) + 50;
2084
- const randomY = Math.floor(Math.random() * 500) + 50;
2085
- await page.mouse.move(randomX, randomY, { steps: 10 });
2086
- await page.mouse.move(randomX + 50, randomY + 50, { steps: 15 });
2087
- await page.mouse.click(randomX + 25, randomY + 25);
2088
- await page.hover('body');
2093
+ // Use enhanced interaction module
2094
+ await performPageInteraction(page, currentUrl, interactionConfig, forceDebug);
2089
2095
  }
2090
2096
 
2091
2097
  const delayMs = siteConfig.delay || 4000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanboynz/network-scanner",
3
- "version": "1.0.45",
3
+ "version": "1.0.46",
4
4
  "description": "A Puppeteer-based network scanner for analyzing web traffic, generating adblock filter rules, and identifying third-party requests. Features include fingerprint spoofing, Cloudflare bypass, content analysis with curl/grep, and multiple output formats.",
5
5
  "main": "nwss.js",
6
6
  "scripts": {