@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.
- package/lib/interaction.js +920 -0
- package/nwss.js +14 -8
- package/package.json +1 -1
|
@@ -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.
|
|
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.
|
|
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
|
-
|
|
2084
|
-
|
|
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.
|
|
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": {
|