unrealon 1.1.6__py3-none-any.whl → 2.0.5__py3-none-any.whl

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.
Files changed (144) hide show
  1. {unrealon-1.1.6.dist-info/licenses → unrealon-2.0.5.dist-info}/LICENSE +1 -1
  2. unrealon-2.0.5.dist-info/METADATA +491 -0
  3. unrealon-2.0.5.dist-info/RECORD +128 -0
  4. {unrealon-1.1.6.dist-info → unrealon-2.0.5.dist-info}/WHEEL +2 -1
  5. unrealon-2.0.5.dist-info/entry_points.txt +3 -0
  6. unrealon-2.0.5.dist-info/top_level.txt +3 -0
  7. unrealon_browser/__init__.py +5 -6
  8. unrealon_browser/cli/browser_cli.py +18 -9
  9. unrealon_browser/cli/interactive_mode.py +13 -4
  10. unrealon_browser/core/browser_manager.py +29 -16
  11. unrealon_browser/dto/__init__.py +21 -0
  12. unrealon_browser/dto/bot_detection.py +175 -0
  13. unrealon_browser/dto/models/config.py +9 -3
  14. unrealon_browser/managers/__init__.py +1 -1
  15. unrealon_browser/managers/logger_bridge.py +1 -4
  16. unrealon_browser/stealth/__init__.py +27 -0
  17. unrealon_browser/stealth/bypass_techniques.pyc +0 -0
  18. unrealon_browser/stealth/manager.pyc +0 -0
  19. unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
  20. unrealon_browser/stealth/playwright_stealth.pyc +0 -0
  21. unrealon_browser/stealth/scanner_tester.pyc +0 -0
  22. unrealon_browser/stealth/undetected_chrome.pyc +0 -0
  23. unrealon_core/__init__.py +172 -0
  24. unrealon_core/config/__init__.py +16 -0
  25. unrealon_core/config/environment.py +151 -0
  26. unrealon_core/config/urls.py +94 -0
  27. unrealon_core/enums/__init__.py +24 -0
  28. unrealon_core/enums/status.py +216 -0
  29. unrealon_core/enums/types.py +240 -0
  30. unrealon_core/error_handling/__init__.py +45 -0
  31. unrealon_core/error_handling/circuit_breaker.py +292 -0
  32. unrealon_core/error_handling/error_context.py +324 -0
  33. unrealon_core/error_handling/recovery.py +371 -0
  34. unrealon_core/error_handling/retry.py +268 -0
  35. unrealon_core/exceptions/__init__.py +46 -0
  36. unrealon_core/exceptions/base.py +292 -0
  37. unrealon_core/exceptions/communication.py +22 -0
  38. unrealon_core/exceptions/driver.py +11 -0
  39. unrealon_core/exceptions/proxy.py +11 -0
  40. unrealon_core/exceptions/task.py +12 -0
  41. unrealon_core/exceptions/validation.py +17 -0
  42. unrealon_core/models/__init__.py +79 -0
  43. unrealon_core/models/arq_context.py +252 -0
  44. unrealon_core/models/arq_responses.py +125 -0
  45. unrealon_core/models/base.py +291 -0
  46. unrealon_core/models/bridge_stats.py +58 -0
  47. unrealon_core/models/communication.py +39 -0
  48. unrealon_core/models/connection_stats.py +47 -0
  49. unrealon_core/models/driver.py +30 -0
  50. unrealon_core/models/driver_details.py +98 -0
  51. unrealon_core/models/logging.py +28 -0
  52. unrealon_core/models/task.py +21 -0
  53. unrealon_core/models/typed_responses.py +210 -0
  54. unrealon_core/models/websocket/__init__.py +91 -0
  55. unrealon_core/models/websocket/base.py +49 -0
  56. unrealon_core/models/websocket/config.py +200 -0
  57. unrealon_core/models/websocket/driver.py +215 -0
  58. unrealon_core/models/websocket/errors.py +138 -0
  59. unrealon_core/models/websocket/heartbeat.py +100 -0
  60. unrealon_core/models/websocket/logging.py +261 -0
  61. unrealon_core/models/websocket/proxy.py +496 -0
  62. unrealon_core/models/websocket/tasks.py +275 -0
  63. unrealon_core/models/websocket/utils.py +153 -0
  64. unrealon_core/models/websocket_session.py +144 -0
  65. unrealon_core/monitoring/__init__.py +43 -0
  66. unrealon_core/monitoring/alerts.py +398 -0
  67. unrealon_core/monitoring/dashboard.py +307 -0
  68. unrealon_core/monitoring/health_check.py +354 -0
  69. unrealon_core/monitoring/metrics.py +352 -0
  70. unrealon_core/utils/__init__.py +11 -0
  71. unrealon_core/utils/time.py +61 -0
  72. unrealon_core/version.py +219 -0
  73. unrealon_driver/__init__.py +90 -51
  74. unrealon_driver/core_module/__init__.py +34 -0
  75. unrealon_driver/core_module/base.py +184 -0
  76. unrealon_driver/core_module/config.py +30 -0
  77. unrealon_driver/core_module/event_manager.py +127 -0
  78. unrealon_driver/core_module/protocols.py +98 -0
  79. unrealon_driver/core_module/registry.py +146 -0
  80. unrealon_driver/decorators/__init__.py +15 -0
  81. unrealon_driver/decorators/retry.py +117 -0
  82. unrealon_driver/decorators/schedule.py +137 -0
  83. unrealon_driver/decorators/task.py +61 -0
  84. unrealon_driver/decorators/timing.py +132 -0
  85. unrealon_driver/driver/__init__.py +20 -0
  86. unrealon_driver/driver/communication/__init__.py +10 -0
  87. unrealon_driver/driver/communication/session.py +203 -0
  88. unrealon_driver/driver/communication/websocket_client.py +205 -0
  89. unrealon_driver/driver/core/__init__.py +10 -0
  90. unrealon_driver/driver/core/config.py +175 -0
  91. unrealon_driver/driver/core/driver.py +221 -0
  92. unrealon_driver/driver/factory/__init__.py +9 -0
  93. unrealon_driver/driver/factory/manager_factory.py +130 -0
  94. unrealon_driver/driver/lifecycle/__init__.py +11 -0
  95. unrealon_driver/driver/lifecycle/daemon.py +76 -0
  96. unrealon_driver/driver/lifecycle/initialization.py +97 -0
  97. unrealon_driver/driver/lifecycle/shutdown.py +48 -0
  98. unrealon_driver/driver/monitoring/__init__.py +9 -0
  99. unrealon_driver/driver/monitoring/health.py +63 -0
  100. unrealon_driver/driver/utilities/__init__.py +10 -0
  101. unrealon_driver/driver/utilities/logging.py +51 -0
  102. unrealon_driver/driver/utilities/serialization.py +61 -0
  103. unrealon_driver/managers/__init__.py +32 -0
  104. unrealon_driver/managers/base.py +174 -0
  105. unrealon_driver/managers/browser.py +98 -0
  106. unrealon_driver/managers/cache.py +116 -0
  107. unrealon_driver/managers/http.py +107 -0
  108. unrealon_driver/managers/logger.py +286 -0
  109. unrealon_driver/managers/proxy.py +99 -0
  110. unrealon_driver/managers/registry.py +87 -0
  111. unrealon_driver/managers/threading.py +54 -0
  112. unrealon_driver/managers/update.py +107 -0
  113. unrealon_driver/utils/__init__.py +9 -0
  114. unrealon_driver/utils/time.py +10 -0
  115. unrealon-1.1.6.dist-info/METADATA +0 -625
  116. unrealon-1.1.6.dist-info/RECORD +0 -55
  117. unrealon-1.1.6.dist-info/entry_points.txt +0 -9
  118. unrealon_browser/managers/stealth.py +0 -388
  119. unrealon_driver/README.md +0 -0
  120. unrealon_driver/exceptions.py +0 -33
  121. unrealon_driver/html_analyzer/__init__.py +0 -32
  122. unrealon_driver/html_analyzer/cleaner.py +0 -657
  123. unrealon_driver/html_analyzer/config.py +0 -64
  124. unrealon_driver/html_analyzer/manager.py +0 -247
  125. unrealon_driver/html_analyzer/models.py +0 -115
  126. unrealon_driver/html_analyzer/websocket_analyzer.py +0 -157
  127. unrealon_driver/models/__init__.py +0 -31
  128. unrealon_driver/models/websocket.py +0 -98
  129. unrealon_driver/parser/__init__.py +0 -36
  130. unrealon_driver/parser/cli_manager.py +0 -142
  131. unrealon_driver/parser/daemon_manager.py +0 -403
  132. unrealon_driver/parser/managers/__init__.py +0 -25
  133. unrealon_driver/parser/managers/config.py +0 -293
  134. unrealon_driver/parser/managers/error.py +0 -412
  135. unrealon_driver/parser/managers/result.py +0 -321
  136. unrealon_driver/parser/parser_manager.py +0 -458
  137. unrealon_driver/smart_logging/__init__.py +0 -24
  138. unrealon_driver/smart_logging/models.py +0 -44
  139. unrealon_driver/smart_logging/smart_logger.py +0 -406
  140. unrealon_driver/smart_logging/unified_logger.py +0 -525
  141. unrealon_driver/websocket/__init__.py +0 -31
  142. unrealon_driver/websocket/client.py +0 -249
  143. unrealon_driver/websocket/config.py +0 -188
  144. unrealon_driver/websocket/manager.py +0 -90
@@ -13,11 +13,19 @@ from rich.panel import Panel
13
13
  from rich.table import Table
14
14
 
15
15
  # Use existing unrealon_browser API
16
- from unrealon_browser import BrowserManager, BrowserConfig, BrowserType, BrowserMode
16
+ from unrealon_browser.core.browser_manager import BrowserManager
17
+ from unrealon_browser.dto.models.config import BrowserConfig, BrowserType, BrowserMode
18
+
19
+ from unrealon_core.config.urls import get_url_config
17
20
 
18
21
  console = Console()
19
22
 
20
23
 
24
+ def _get_default_test_url() -> str:
25
+ """Get default test URL from configuration."""
26
+ return get_url_config().stealth_test_url
27
+
28
+
21
29
  @click.group()
22
30
  def browser():
23
31
  """🌐 Browser automation commands."""
@@ -46,9 +54,10 @@ def launch(parser, browser_type, headless, stealth, url):
46
54
 
47
55
  # Interactive mode if no URL provided
48
56
  if not url:
49
- url = questionary.text("Enter URL to navigate:", default="https://bot.sannysoft.com").ask()
57
+ default_url = _get_default_test_url()
58
+ url = questionary.text("Enter URL to navigate:", default=default_url).ask()
50
59
  if not url: # If user just pressed Enter with empty input
51
- url = "https://bot.sannysoft.com"
60
+ url = default_url
52
61
 
53
62
  # Run browser session
54
63
  asyncio.run(_run_browser_session(parser, browser_type, headless, stealth, url))
@@ -56,7 +65,7 @@ def launch(parser, browser_type, headless, stealth, url):
56
65
 
57
66
  @browser.command()
58
67
  @click.option("--parser", default="default_parser", help="Parser name")
59
- @click.option("--url", default="https://bot.sannysoft.com", help="Test URL")
68
+ @click.option("--url", default=None, help="Test URL")
60
69
  def stealth_test(parser, url):
61
70
  """🕵️ Test stealth effectiveness."""
62
71
  console.print("[bold blue]🕵️ Testing stealth capabilities...[/bold blue]")
@@ -109,9 +118,10 @@ def _interactive_launch():
109
118
  browser_type = questionary.select("Browser type:", choices=["chromium", "firefox", "webkit"]).ask()
110
119
  headless = questionary.confirm("Headless mode?", default=False).ask()
111
120
  stealth = questionary.select("Stealth level:", choices=["disabled", "basic", "advanced"]).ask()
112
- url = questionary.text("URL to navigate:", default="https://bot.sannysoft.com").ask()
121
+ default_url = _get_default_test_url()
122
+ url = questionary.text("URL to navigate:", default=default_url).ask()
113
123
  if not url:
114
- url = "https://bot.sannysoft.com"
124
+ url = default_url
115
125
 
116
126
  asyncio.run(_run_browser_session(parser, browser_type, headless, stealth, url))
117
127
 
@@ -119,7 +129,8 @@ def _interactive_launch():
119
129
  def _interactive_stealth_test():
120
130
  """Interactive stealth test."""
121
131
  parser = questionary.text("Parser name:", default="default_parser").ask()
122
- url = questionary.text("Test URL:", default="https://bot.sannysoft.com").ask()
132
+ default_url = _get_default_test_url()
133
+ url = questionary.text("Test URL:", default=default_url).ask()
123
134
 
124
135
  asyncio.run(_run_stealth_test(parser, url))
125
136
 
@@ -157,7 +168,6 @@ async def _run_browser_session(parser: str, browser_type: str, headless: bool, s
157
168
  parser_name=parser,
158
169
  browser_type=BrowserType(browser_type.lower()),
159
170
  mode=BrowserMode.HEADLESS if headless else BrowserMode.HEADED,
160
- # stealth_level removed - STEALTH ALWAYS ON!
161
171
  )
162
172
 
163
173
  # Use existing BrowserManager - no duplication!
@@ -194,7 +204,6 @@ async def _run_stealth_test(parser: str, url: str):
194
204
  parser_name=parser,
195
205
  browser_type=BrowserType.CHROMIUM,
196
206
  mode=BrowserMode.HEADLESS,
197
- # stealth_level removed - STEALTH ALWAYS ON!
198
207
  )
199
208
 
200
209
  browser_manager = BrowserManager(config)
@@ -11,9 +11,16 @@ from rich.panel import Panel
11
11
  from rich.table import Table
12
12
  from typing import Dict, Any
13
13
 
14
+ from unrealon_core.config.urls import get_url_config
15
+
14
16
  console = Console()
15
17
 
16
18
 
19
+ def _get_default_test_url() -> str:
20
+ """Get default test URL from configuration."""
21
+ return get_url_config().stealth_test_url
22
+
23
+
17
24
  async def run_interactive_mode(parser: str, verbose: bool = False) -> None:
18
25
  """
19
26
  Run fully interactive browser management mode.
@@ -100,12 +107,13 @@ async def _interactive_browser_launch(parser: str, verbose: bool) -> None:
100
107
  # 🔥 STEALTH ALWAYS ON - NO CONFIG NEEDED!
101
108
  stealth_info = questionary.select(
102
109
  "Stealth is always enabled. Select stealth verification:",
103
- choices=["None", "Test on bot.sannysoft.com", "Test on fingerprint.com"]
110
+ choices=["None", "Test on detection service", "Test on fingerprint.com"]
104
111
  ).ask()
105
112
 
113
+ default_url = _get_default_test_url()
106
114
  url = questionary.text(
107
115
  "Enter URL to navigate:",
108
- default="https://bot.sannysoft.com"
116
+ default=default_url
109
117
  ).ask()
110
118
 
111
119
  # Show configuration summary
@@ -136,9 +144,10 @@ async def _interactive_stealth_test(parser: str, verbose: bool) -> None:
136
144
  """Interactive stealth testing."""
137
145
  console.print("\n[bold blue]🕵️ Stealth Testing[/bold blue]")
138
146
 
147
+ default_url = _get_default_test_url()
139
148
  test_url = questionary.text(
140
149
  "Enter test URL:",
141
- default="https://bot.sannysoft.com"
150
+ default=default_url
142
151
  ).ask()
143
152
 
144
153
  test_levels = questionary.checkbox(
@@ -330,7 +339,7 @@ def _show_help_information() -> None:
330
339
 
331
340
  [yellow]Examples:[/yellow]
332
341
  [dim]cli-browser launch --parser my_parser --headless --url https://example.com
333
- cli-browser stealth-test --url https://bot.sannysoft.com
342
+ cli-browser stealth-test --url {_get_default_test_url()}
334
343
  cli-browser workflow --parser scraper --url https://target-site.com[/dim]
335
344
 
336
345
  [yellow]For more information:[/yellow]
@@ -55,14 +55,16 @@ class BrowserManager:
55
55
  self._initialized = False
56
56
  self._statistics = BrowserManagerStatistics()
57
57
 
58
+ # Initialize logger bridge first
59
+ self.logger_bridge = create_browser_logger_bridge(session_id=self._generate_session_id(), parser_id=self.parser_id, enable_console=True) # Use resolved parser_id
60
+
58
61
  # Initialize managers
59
- self.stealth_manager = StealthManager()
62
+ self.stealth_manager = StealthManager(logger_bridge=self.logger_bridge)
60
63
  # ✅ FIX: Don't create default managers here - they will be set by Browser service
61
64
  # This prevents duplicate directory creation with wrong paths
62
65
  self.profile_manager = None
63
66
  self.cookie_manager = None
64
67
  self.captcha_manager = CaptchaDetector()
65
- self.logger_bridge = create_browser_logger_bridge(session_id=self._generate_session_id(), parser_id=self.parser_id, enable_console=True) # Use resolved parser_id
66
68
  self.page_wait = PageWaitManager(None, self.logger_bridge)
67
69
 
68
70
  # Signal handlers for graceful shutdown
@@ -226,13 +228,13 @@ class BrowserManager:
226
228
 
227
229
  # Create page
228
230
  self._page = await self._context.new_page()
229
-
231
+
230
232
  # Update page wait manager with new page
231
233
  self.page_wait.update_page(self._page)
232
234
 
233
235
  # 🔥 STEALTH ALWAYS APPLIED TO EVERY PAGE!
234
236
  stealth_success = await self.stealth_manager.apply_stealth(self._page)
235
- self.logger_bridge.log_stealth_applied("ALWAYS_ON", stealth_success)
237
+ self.logger_bridge.log_stealth_applied(stealth_success)
236
238
 
237
239
  # 🔥 CRITICAL: If stealth fails, CLOSE BROWSER WITH ERROR!
238
240
  if not stealth_success:
@@ -302,34 +304,34 @@ class BrowserManager:
302
304
  async def _navigate_with_stealth_retry(self, url: str, wait_for: Optional[str] = None) -> Dict[str, Any]:
303
305
  """Navigate with stealth retry logic - universal for all sites"""
304
306
  self.logger_bridge.log_info(f"🥷 Stealth navigation to: {url}")
305
-
307
+
306
308
  # Stealth warmup if enabled
307
309
  if self.config.stealth_warmup_enabled:
308
310
  self.logger_bridge.log_info(f"🧪 First visiting stealth test page: {self.config.stealth_test_url}")
309
311
  await self._navigate_basic(self.config.stealth_test_url, None)
310
312
  self.logger_bridge.log_info(f"⏳ Waiting {self.config.stealth_warmup_delay} seconds for stealth establishment...")
311
313
  await asyncio.sleep(self.config.stealth_warmup_delay)
312
-
314
+
313
315
  # Now navigate to the actual target URL with retry logic
314
316
  max_retries = self.config.stealth_retry_attempts
315
317
  for attempt in range(max_retries + 1):
316
318
  if attempt > 0:
317
319
  self.logger_bridge.log_info(f"🔄 Stealth retry attempt {attempt}/{max_retries}")
318
320
  await asyncio.sleep(self.config.stealth_retry_delay)
319
-
321
+
320
322
  result = await self._navigate_basic(url, wait_for)
321
-
323
+
322
324
  # If successful, return result
323
325
  if result["success"]:
324
326
  if attempt > 0:
325
327
  self.logger_bridge.log_info(f"✅ Stealth retry successful on attempt {attempt + 1}")
326
328
  return result
327
-
329
+
328
330
  # If failed and we have retries left, continue
329
331
  if attempt < max_retries:
330
332
  self.logger_bridge.log_info(f"⚠️ Navigation failed, will retry in {self.config.stealth_retry_delay} seconds...")
331
333
  continue
332
-
334
+
333
335
  # All retries failed
334
336
  self.logger_bridge.log_warning(f"❌ Stealth navigation failed after {max_retries + 1} attempts")
335
337
  return result
@@ -411,10 +413,6 @@ class BrowserManager:
411
413
  "error": str(e),
412
414
  }
413
415
 
414
- async def wait_for_page_ready_async(self, wait_type: str = "networkidle", timeout: int = 10000) -> bool:
415
- """Wait for page to be ready for parsing (legacy method - use page_wait.* methods instead)"""
416
- return await self.page_wait.wait_custom(wait_type, timeout)
417
-
418
416
  async def get_page_content_async(self) -> Optional[str]:
419
417
  """Get current page content"""
420
418
  if not self._page:
@@ -483,8 +481,23 @@ class BrowserManager:
483
481
  self.logger_bridge.print_statistics()
484
482
 
485
483
  async def test_stealth_async(self) -> Dict[str, Any]:
486
- """Test stealth effectiveness on bot.sannysoft.com"""
487
- return await self.stealth_manager.test_stealth_on_sannysoft(self)
484
+ """Test stealth effectiveness on detection service"""
485
+ self.logger_bridge.log_info("🔍 DEBUG: test_stealth_async called")
486
+ self.logger_bridge.log_info(f"🔍 DEBUG: stealth_manager={self.stealth_manager}")
487
+ try:
488
+ self.logger_bridge.log_info("🔍 DEBUG: About to call stealth_manager.test_stealth_effectiveness")
489
+ result = await self.stealth_manager.test_stealth_effectiveness(self)
490
+ self.logger_bridge.log_info(f"🔍 DEBUG: stealth_manager.test_stealth_effectiveness returned: {result}")
491
+ return result
492
+ except Exception as e:
493
+ self.logger_bridge.log_error(f"❌ DEBUG: Exception in test_stealth_async: {e}")
494
+ import traceback
495
+ self.logger_bridge.log_error(f"❌ DEBUG: Traceback: {traceback.format_exc()}")
496
+ return {
497
+ "success": False,
498
+ "error": str(e),
499
+ "skipped": False,
500
+ }
488
501
 
489
502
  async def detect_captcha_async(self) -> Dict[str, Any]:
490
503
  """Detect captcha on current page"""
@@ -39,6 +39,18 @@ from .models.detection import (
39
39
  CookieMetadata,
40
40
  )
41
41
 
42
+ # Bot detection models
43
+ from .bot_detection import (
44
+ TestResult,
45
+ BotDetectionSummary,
46
+ BotDetectionResults,
47
+ BotDetectionError,
48
+ BotDetectionResponse,
49
+ BotTestResult,
50
+ BotSummary,
51
+ BotResults,
52
+ )
53
+
42
54
 
43
55
  # Exports
44
56
  __all__ = [
@@ -62,4 +74,13 @@ __all__ = [
62
74
  "CaptchaDetection",
63
75
  "CaptchaDetectionResult",
64
76
  "CookieMetadata",
77
+ # Bot detection models
78
+ "TestResult",
79
+ "BotDetectionSummary",
80
+ "BotDetectionResults",
81
+ "BotDetectionError",
82
+ "BotDetectionResponse",
83
+ "BotTestResult",
84
+ "BotSummary",
85
+ "BotResults",
65
86
  ]
@@ -0,0 +1,175 @@
1
+ """
2
+ Bot Detection Results DTOs
3
+
4
+ Pydantic models for bot detection results from the frontend scanner.
5
+ Based on TypeScript interfaces from botDetection.ts
6
+ """
7
+
8
+ from typing import Any, Dict, List, Literal, Optional, Union
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class TestResult(BaseModel):
13
+ """Individual test result from bot detection"""
14
+
15
+ name: str = Field(..., description="Name of the test")
16
+ status: Literal["passed", "failed", "warn"] = Field(..., description="Test status")
17
+ value: Any = Field(..., description="Test result value (can be any type)")
18
+ description: str = Field(..., description="Human-readable test description")
19
+ score: int = Field(..., ge=0, le=100, description="Suspicion score (0-100, higher = more suspicious)")
20
+
21
+
22
+ class BotDetectionSummary(BaseModel):
23
+ """Summary statistics for bot detection tests"""
24
+
25
+ passed: int = Field(..., ge=0, description="Number of tests that passed")
26
+ failed: int = Field(..., ge=0, description="Number of tests that failed")
27
+ warnings: int = Field(..., ge=0, description="Number of tests with warnings")
28
+ total: int = Field(..., ge=0, description="Total number of tests run")
29
+
30
+
31
+ class BotDetectionResults(BaseModel):
32
+ """Complete bot detection results from frontend scanner"""
33
+
34
+ tests: List[TestResult] = Field(..., description="List of individual test results")
35
+ overall_score: int = Field(..., ge=0, le=100, description="Overall suspicion score (0-100)", alias="overallScore")
36
+ is_bot: bool = Field(..., description="Whether the browser is detected as a bot", alias="isBot")
37
+ confidence: Literal["low", "medium", "high"] = Field(..., description="Confidence level of detection")
38
+ summary: BotDetectionSummary = Field(..., description="Summary statistics")
39
+
40
+ model_config = {
41
+ "json_encoders": {
42
+ # Handle Any type serialization
43
+ Any: lambda v: v
44
+ },
45
+ # Allow population by field name or alias
46
+ "populate_by_name": True
47
+ }
48
+
49
+ @property
50
+ def failed_tests(self) -> List[TestResult]:
51
+ """Get list of failed tests"""
52
+ return [test for test in self.tests if test.status == "failed"]
53
+
54
+ @property
55
+ def critical_failures(self) -> List[TestResult]:
56
+ """Get list of critical failures that strongly indicate bot detection"""
57
+ critical_test_names = [
58
+ "BotD Detection",
59
+ "WebDriver Property",
60
+ "Headless Chrome",
61
+ "Chrome Object Consistency",
62
+ "Navigator WebDriver",
63
+ "Advanced Automation Detection"
64
+ ]
65
+
66
+ return [
67
+ test for test in self.failed_tests
68
+ if any(critical in test.name for critical in critical_test_names)
69
+ ]
70
+
71
+ @property
72
+ def warning_tests(self) -> List[TestResult]:
73
+ """Get list of tests with warnings"""
74
+ return [test for test in self.tests if test.status == "warn"]
75
+
76
+ @property
77
+ def passed_tests(self) -> List[TestResult]:
78
+ """Get list of passed tests"""
79
+ return [test for test in self.tests if test.status == "passed"]
80
+
81
+ def get_effectiveness_rating(self) -> str:
82
+ """Get stealth effectiveness rating based on results"""
83
+ if self.overall_score <= 10 and not self.is_bot:
84
+ return "excellent"
85
+ elif self.overall_score <= 25 and not self.is_bot:
86
+ return "good"
87
+ elif self.overall_score <= 50:
88
+ return "moderate"
89
+ else:
90
+ return "poor"
91
+
92
+ def get_recommendations(self) -> List[str]:
93
+ """Generate recommendations based on test results"""
94
+ recommendations = []
95
+
96
+ failed_test_names = [test.name for test in self.failed_tests]
97
+
98
+ if any("BotD" in name for name in failed_test_names):
99
+ recommendations.append("Consider using headed mode instead of headless for better BotD bypass")
100
+
101
+ if any("WebDriver" in name for name in failed_test_names):
102
+ recommendations.append("Enhance webdriver property removal techniques")
103
+
104
+ if any("Chrome Object" in name for name in failed_test_names):
105
+ recommendations.append("Improve window.chrome object spoofing")
106
+
107
+ if any("Plugin" in name for name in failed_test_names):
108
+ recommendations.append("Add more realistic browser plugin simulation")
109
+
110
+ if any("WebGL" in name for name in failed_test_names):
111
+ recommendations.append("Enhance WebGL vendor/renderer spoofing")
112
+
113
+ if self.overall_score > 30:
114
+ recommendations.append("Consider using undetected-chromedriver or NoDriver for better results")
115
+
116
+ if len(self.critical_failures) > 2:
117
+ recommendations.append("Multiple critical failures detected - review stealth configuration")
118
+
119
+ return recommendations
120
+
121
+
122
+ class BotDetectionError(BaseModel):
123
+ """Error information when bot detection fails"""
124
+
125
+ error: str = Field(..., description="Error message")
126
+ error_type: str = Field(default="detection_error", description="Type of error")
127
+ timestamp: Optional[str] = Field(None, description="When the error occurred")
128
+
129
+
130
+ class BotDetectionResponse(BaseModel):
131
+ """Response wrapper for bot detection results"""
132
+
133
+ success: bool = Field(..., description="Whether detection was successful")
134
+ results: Optional[BotDetectionResults] = Field(None, description="Detection results if successful")
135
+ error: Optional[BotDetectionError] = Field(None, description="Error information if failed")
136
+ scanner_url: Optional[str] = Field(None, description="URL of the scanner used")
137
+ method: Optional[str] = Field(None, description="Stealth method used")
138
+ timestamp: Optional[str] = Field(None, description="When the detection was performed")
139
+
140
+ @classmethod
141
+ def success_response(
142
+ cls,
143
+ results: BotDetectionResults,
144
+ scanner_url: Optional[str] = None,
145
+ method: Optional[str] = None
146
+ ) -> "BotDetectionResponse":
147
+ """Create a successful response"""
148
+ return cls(
149
+ success=True,
150
+ results=results,
151
+ scanner_url=scanner_url,
152
+ method=method
153
+ )
154
+
155
+ @classmethod
156
+ def error_response(
157
+ cls,
158
+ error_message: str,
159
+ error_type: str = "detection_error",
160
+ scanner_url: Optional[str] = None,
161
+ method: Optional[str] = None
162
+ ) -> "BotDetectionResponse":
163
+ """Create an error response"""
164
+ return cls(
165
+ success=False,
166
+ error=BotDetectionError(error=error_message, error_type=error_type),
167
+ scanner_url=scanner_url,
168
+ method=method
169
+ )
170
+
171
+
172
+ # Type aliases for convenience
173
+ BotTestResult = TestResult
174
+ BotSummary = BotDetectionSummary
175
+ BotResults = BotDetectionResults
@@ -8,6 +8,13 @@ from typing import Optional
8
8
  from pydantic import BaseModel, Field, ConfigDict
9
9
  from .enums import BrowserType, BrowserMode
10
10
 
11
+ from unrealon_core.config.urls import get_url_config
12
+
13
+
14
+ def _get_default_stealth_url() -> str:
15
+ """Get default stealth test URL based on environment configuration."""
16
+ return get_url_config().stealth_test_url
17
+
11
18
 
12
19
  class BrowserConfig(BaseModel):
13
20
  """Simplified browser configuration."""
@@ -30,11 +37,10 @@ class BrowserConfig(BaseModel):
30
37
 
31
38
  # Performance
32
39
  disable_images: bool = Field(default=False)
33
- enable_stealth_check: bool = Field(default=False)
34
40
 
35
41
  # Stealth settings
36
42
  stealth_warmup_enabled: bool = Field(default=True, description="Enable stealth warmup before target navigation")
37
- stealth_test_url: str = Field(default="https://bot.sannysoft.com", description="URL for stealth warmup")
43
+ stealth_test_url: str = Field(default_factory=_get_default_stealth_url, description="URL for stealth warmup")
38
44
  stealth_warmup_delay: float = Field(default=3.0, description="Delay in seconds after stealth warmup")
39
45
  stealth_retry_attempts: int = Field(default=2, description="Maximum retry attempts for failed navigation")
40
- stealth_retry_delay: float = Field(default=3.0, description="Delay between retry attempts")
46
+ stealth_retry_delay: float = Field(default=3.0, description="Delay between retry attempts")
@@ -2,7 +2,7 @@
2
2
  Browser Managers Package - Specialized management components
3
3
  """
4
4
 
5
- from .stealth import StealthManager
5
+ from ..stealth import StealthManager
6
6
  from .profile import ProfileManager
7
7
  from .logger_bridge import BrowserLoggerBridge, create_browser_logger_bridge
8
8
  from .cookies import CookieManager
@@ -141,7 +141,6 @@ class BrowserLoggerBridge:
141
141
  session_id=metadata.session_id,
142
142
  parser_name=metadata.parser_name,
143
143
  browser_type=metadata.browser_type or "unknown",
144
- stealth_level="ALWAYS_ON", # 🔥 STEALTH ALWAYS ON!
145
144
  proxy_host=getattr(metadata.proxy, "host", None) if metadata.proxy else None,
146
145
  proxy_port=getattr(metadata.proxy, "port", None) if metadata.proxy else None,
147
146
  )
@@ -168,20 +167,18 @@ class BrowserLoggerBridge:
168
167
  navigation_type="browser_navigation",
169
168
  )
170
169
 
171
- def log_stealth_applied(self, stealth_level: str = "ALWAYS_ON", success: bool = True) -> None:
170
+ def log_stealth_applied(self, success: bool = True) -> None:
172
171
  """Log stealth application - 🔥 STEALTH ALWAYS ON!"""
173
172
  self._browser_events["stealth_applied"] += 1
174
173
 
175
174
  if success:
176
175
  self._log_info(
177
176
  "Stealth measures applied: ALWAYS_ON",
178
- stealth_level="ALWAYS_ON",
179
177
  stealth_success=True,
180
178
  )
181
179
  else:
182
180
  self._log_warning(
183
181
  "Stealth application failed: ALWAYS_ON",
184
- stealth_level="ALWAYS_ON",
185
182
  stealth_success=False,
186
183
  )
187
184
 
@@ -0,0 +1,27 @@
1
+ """
2
+ UnrealOn Stealth Package - Advanced anti-detection system
3
+
4
+ Modular bot detection bypass system:
5
+ - PlaywrightStealth: playwright-stealth integration
6
+ - UndetectedChrome: undetected-chromedriver support
7
+ - NoDriverStealth: NoDriver integration
8
+ - BypassTechniques: advanced BotD bypass techniques
9
+ - StealthManager: main coordinator for all techniques
10
+ - ScannerTester: real-world testing through UnrealOn scanner
11
+ """
12
+
13
+ from .manager import StealthManager
14
+ from .playwright_stealth import PlaywrightStealth
15
+ from .undetected_chrome import UndetectedChrome
16
+ from .nodriver_stealth import NoDriverStealth
17
+ from .bypass_techniques import BypassTechniques
18
+ from .scanner_tester import ScannerTester
19
+
20
+ __all__ = [
21
+ "StealthManager",
22
+ "PlaywrightStealth",
23
+ "UndetectedChrome",
24
+ "NoDriverStealth",
25
+ "BypassTechniques",
26
+ "ScannerTester"
27
+ ]
Binary file