unrealon 1.0.9__py3-none-any.whl → 1.1.1__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.
- unrealon/__init__.py +23 -21
- unrealon-1.1.1.dist-info/METADATA +722 -0
- unrealon-1.1.1.dist-info/RECORD +82 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.1.dist-info}/WHEEL +1 -1
- unrealon-1.1.1.dist-info/entry_points.txt +9 -0
- {unrealon-1.0.9.dist-info → unrealon-1.1.1.dist-info/licenses}/LICENSE +1 -1
- unrealon_bridge/__init__.py +114 -0
- unrealon_bridge/cli.py +316 -0
- unrealon_bridge/client/__init__.py +93 -0
- unrealon_bridge/client/base.py +78 -0
- unrealon_bridge/client/commands.py +89 -0
- unrealon_bridge/client/connection.py +90 -0
- unrealon_bridge/client/events.py +65 -0
- unrealon_bridge/client/health.py +38 -0
- unrealon_bridge/client/html_parser.py +146 -0
- unrealon_bridge/client/logging.py +139 -0
- unrealon_bridge/client/proxy.py +70 -0
- unrealon_bridge/client/scheduler.py +450 -0
- unrealon_bridge/client/session.py +70 -0
- unrealon_bridge/configs/__init__.py +14 -0
- unrealon_bridge/configs/bridge_config.py +212 -0
- unrealon_bridge/configs/bridge_config.yaml +39 -0
- unrealon_bridge/models/__init__.py +138 -0
- unrealon_bridge/models/base.py +28 -0
- unrealon_bridge/models/command.py +41 -0
- unrealon_bridge/models/events.py +40 -0
- unrealon_bridge/models/html_parser.py +79 -0
- unrealon_bridge/models/logging.py +55 -0
- unrealon_bridge/models/parser.py +63 -0
- unrealon_bridge/models/proxy.py +41 -0
- unrealon_bridge/models/requests.py +95 -0
- unrealon_bridge/models/responses.py +88 -0
- unrealon_bridge/models/scheduler.py +592 -0
- unrealon_bridge/models/session.py +28 -0
- unrealon_bridge/server/__init__.py +91 -0
- unrealon_bridge/server/base.py +171 -0
- unrealon_bridge/server/handlers/__init__.py +23 -0
- unrealon_bridge/server/handlers/command.py +110 -0
- unrealon_bridge/server/handlers/html_parser.py +139 -0
- unrealon_bridge/server/handlers/logging.py +95 -0
- unrealon_bridge/server/handlers/parser.py +95 -0
- unrealon_bridge/server/handlers/proxy.py +75 -0
- unrealon_bridge/server/handlers/scheduler.py +545 -0
- unrealon_bridge/server/handlers/session.py +66 -0
- unrealon_browser/__init__.py +61 -18
- unrealon_browser/{src/cli → cli}/browser_cli.py +6 -13
- unrealon_browser/{src/cli → cli}/cookies_cli.py +5 -1
- unrealon_browser/{src/core → core}/browser_manager.py +2 -2
- unrealon_browser/{src/managers → managers}/captcha.py +1 -1
- unrealon_browser/{src/managers → managers}/cookies.py +1 -1
- unrealon_browser/managers/logger_bridge.py +231 -0
- unrealon_browser/{src/managers → managers}/profile.py +1 -1
- unrealon_driver/__init__.py +73 -19
- unrealon_driver/browser/__init__.py +8 -0
- unrealon_driver/browser/config.py +74 -0
- unrealon_driver/browser/manager.py +416 -0
- unrealon_driver/exceptions.py +28 -0
- unrealon_driver/parser/__init__.py +55 -0
- unrealon_driver/parser/cli_manager.py +141 -0
- unrealon_driver/parser/daemon_manager.py +227 -0
- unrealon_driver/parser/managers/__init__.py +46 -0
- unrealon_driver/parser/managers/browser.py +51 -0
- unrealon_driver/parser/managers/config.py +281 -0
- unrealon_driver/parser/managers/error.py +412 -0
- unrealon_driver/parser/managers/html.py +732 -0
- unrealon_driver/parser/managers/logging.py +609 -0
- unrealon_driver/parser/managers/result.py +321 -0
- unrealon_driver/parser/parser_manager.py +628 -0
- unrealon/sdk_config.py +0 -88
- unrealon-1.0.9.dist-info/METADATA +0 -810
- unrealon-1.0.9.dist-info/RECORD +0 -246
- unrealon_browser/pyproject.toml +0 -182
- unrealon_browser/src/__init__.py +0 -62
- unrealon_browser/src/managers/logger_bridge.py +0 -395
- unrealon_driver/README.md +0 -204
- unrealon_driver/pyproject.toml +0 -187
- unrealon_driver/src/__init__.py +0 -90
- unrealon_driver/src/cli/__init__.py +0 -10
- unrealon_driver/src/cli/main.py +0 -66
- unrealon_driver/src/cli/simple.py +0 -510
- unrealon_driver/src/config/__init__.py +0 -11
- unrealon_driver/src/config/auto_config.py +0 -478
- unrealon_driver/src/core/__init__.py +0 -18
- unrealon_driver/src/core/exceptions.py +0 -289
- unrealon_driver/src/core/parser.py +0 -638
- unrealon_driver/src/dto/__init__.py +0 -66
- unrealon_driver/src/dto/cli.py +0 -119
- unrealon_driver/src/dto/config.py +0 -18
- unrealon_driver/src/dto/events.py +0 -237
- unrealon_driver/src/dto/execution.py +0 -313
- unrealon_driver/src/dto/services.py +0 -311
- unrealon_driver/src/execution/__init__.py +0 -23
- unrealon_driver/src/execution/daemon_mode.py +0 -317
- unrealon_driver/src/execution/interactive_mode.py +0 -88
- unrealon_driver/src/execution/modes.py +0 -45
- unrealon_driver/src/execution/scheduled_mode.py +0 -209
- unrealon_driver/src/execution/test_mode.py +0 -250
- unrealon_driver/src/logging/__init__.py +0 -24
- unrealon_driver/src/logging/driver_logger.py +0 -512
- unrealon_driver/src/services/__init__.py +0 -24
- unrealon_driver/src/services/browser_service.py +0 -726
- unrealon_driver/src/services/llm/__init__.py +0 -15
- unrealon_driver/src/services/llm/browser_llm_service.py +0 -363
- unrealon_driver/src/services/llm/llm.py +0 -195
- unrealon_driver/src/services/logger_service.py +0 -232
- unrealon_driver/src/services/metrics_service.py +0 -185
- unrealon_driver/src/services/scheduler_service.py +0 -489
- unrealon_driver/src/services/websocket_service.py +0 -362
- unrealon_driver/src/utils/__init__.py +0 -16
- unrealon_driver/src/utils/service_factory.py +0 -317
- unrealon_driver/src/utils/time_formatter.py +0 -338
- unrealon_llm/README.md +0 -44
- unrealon_llm/__init__.py +0 -26
- unrealon_llm/pyproject.toml +0 -154
- unrealon_llm/src/__init__.py +0 -228
- unrealon_llm/src/cli/__init__.py +0 -0
- unrealon_llm/src/core/__init__.py +0 -11
- unrealon_llm/src/core/smart_client.py +0 -438
- unrealon_llm/src/dto/__init__.py +0 -155
- unrealon_llm/src/dto/models/__init__.py +0 -0
- unrealon_llm/src/dto/models/config.py +0 -343
- unrealon_llm/src/dto/models/core.py +0 -328
- unrealon_llm/src/dto/models/enums.py +0 -123
- unrealon_llm/src/dto/models/html_analysis.py +0 -345
- unrealon_llm/src/dto/models/statistics.py +0 -473
- unrealon_llm/src/dto/models/translation.py +0 -383
- unrealon_llm/src/dto/models/type_conversion.py +0 -462
- unrealon_llm/src/dto/schemas/__init__.py +0 -0
- unrealon_llm/src/exceptions.py +0 -392
- unrealon_llm/src/llm_config/__init__.py +0 -20
- unrealon_llm/src/llm_config/logging_config.py +0 -178
- unrealon_llm/src/llm_logging/__init__.py +0 -42
- unrealon_llm/src/llm_logging/llm_events.py +0 -107
- unrealon_llm/src/llm_logging/llm_logger.py +0 -466
- unrealon_llm/src/managers/__init__.py +0 -15
- unrealon_llm/src/managers/cache_manager.py +0 -67
- unrealon_llm/src/managers/cost_manager.py +0 -107
- unrealon_llm/src/managers/request_manager.py +0 -298
- unrealon_llm/src/modules/__init__.py +0 -0
- unrealon_llm/src/modules/html_processor/__init__.py +0 -25
- unrealon_llm/src/modules/html_processor/base_processor.py +0 -415
- unrealon_llm/src/modules/html_processor/details_processor.py +0 -85
- unrealon_llm/src/modules/html_processor/listing_processor.py +0 -91
- unrealon_llm/src/modules/html_processor/models/__init__.py +0 -20
- unrealon_llm/src/modules/html_processor/models/processing_models.py +0 -40
- unrealon_llm/src/modules/html_processor/models/universal_model.py +0 -56
- unrealon_llm/src/modules/html_processor/processor.py +0 -102
- unrealon_llm/src/modules/llm/__init__.py +0 -0
- unrealon_llm/src/modules/translator/__init__.py +0 -0
- unrealon_llm/src/provider.py +0 -116
- unrealon_llm/src/utils/__init__.py +0 -95
- unrealon_llm/src/utils/common.py +0 -64
- unrealon_llm/src/utils/data_extractor.py +0 -188
- unrealon_llm/src/utils/html_cleaner.py +0 -767
- unrealon_llm/src/utils/language_detector.py +0 -308
- unrealon_llm/src/utils/models_cache.py +0 -592
- unrealon_llm/src/utils/smart_counter.py +0 -229
- unrealon_llm/src/utils/token_counter.py +0 -189
- unrealon_sdk/README.md +0 -25
- unrealon_sdk/__init__.py +0 -30
- unrealon_sdk/pyproject.toml +0 -231
- unrealon_sdk/src/__init__.py +0 -150
- unrealon_sdk/src/cli/__init__.py +0 -12
- unrealon_sdk/src/cli/commands/__init__.py +0 -22
- unrealon_sdk/src/cli/commands/benchmark.py +0 -42
- unrealon_sdk/src/cli/commands/diagnostics.py +0 -573
- unrealon_sdk/src/cli/commands/health.py +0 -46
- unrealon_sdk/src/cli/commands/integration.py +0 -498
- unrealon_sdk/src/cli/commands/reports.py +0 -43
- unrealon_sdk/src/cli/commands/security.py +0 -36
- unrealon_sdk/src/cli/commands/server.py +0 -483
- unrealon_sdk/src/cli/commands/servers.py +0 -56
- unrealon_sdk/src/cli/commands/tests.py +0 -55
- unrealon_sdk/src/cli/main.py +0 -126
- unrealon_sdk/src/cli/utils/reporter.py +0 -519
- unrealon_sdk/src/clients/openapi.yaml +0 -3347
- unrealon_sdk/src/clients/python_http/__init__.py +0 -3
- unrealon_sdk/src/clients/python_http/api_config.py +0 -228
- unrealon_sdk/src/clients/python_http/models/BaseModel.py +0 -12
- unrealon_sdk/src/clients/python_http/models/BroadcastDeliveryStats.py +0 -33
- unrealon_sdk/src/clients/python_http/models/BroadcastMessage.py +0 -17
- unrealon_sdk/src/clients/python_http/models/BroadcastMessageRequest.py +0 -35
- unrealon_sdk/src/clients/python_http/models/BroadcastPriority.py +0 -10
- unrealon_sdk/src/clients/python_http/models/BroadcastResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/BroadcastResultResponse.py +0 -33
- unrealon_sdk/src/clients/python_http/models/BroadcastTarget.py +0 -11
- unrealon_sdk/src/clients/python_http/models/ConnectionStats.py +0 -27
- unrealon_sdk/src/clients/python_http/models/ConnectionsResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/DeveloperMessageResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ErrorResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/HTTPValidationError.py +0 -16
- unrealon_sdk/src/clients/python_http/models/HealthResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/HealthStatus.py +0 -33
- unrealon_sdk/src/clients/python_http/models/LogLevel.py +0 -10
- unrealon_sdk/src/clients/python_http/models/LoggingRequest.py +0 -27
- unrealon_sdk/src/clients/python_http/models/LoggingResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/MaintenanceMode.py +0 -9
- unrealon_sdk/src/clients/python_http/models/MaintenanceModeRequest.py +0 -33
- unrealon_sdk/src/clients/python_http/models/MaintenanceStatusResponse.py +0 -39
- unrealon_sdk/src/clients/python_http/models/ParserCommandRequest.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ParserMessageResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/ParserRegistrationRequest.py +0 -28
- unrealon_sdk/src/clients/python_http/models/ParserRegistrationResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ParserType.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyBlockRequest.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyEndpointResponse.py +0 -20
- unrealon_sdk/src/clients/python_http/models/ProxyListResponse.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyProvider.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyPurchaseRequest.py +0 -25
- unrealon_sdk/src/clients/python_http/models/ProxyResponse.py +0 -47
- unrealon_sdk/src/clients/python_http/models/ProxyRotationRequest.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ProxyStatus.py +0 -10
- unrealon_sdk/src/clients/python_http/models/ProxyUsageRequest.py +0 -19
- unrealon_sdk/src/clients/python_http/models/ProxyUsageStatsResponse.py +0 -26
- unrealon_sdk/src/clients/python_http/models/ServiceRegistrationDto.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ServiceStatsResponse.py +0 -31
- unrealon_sdk/src/clients/python_http/models/SessionStartRequest.py +0 -23
- unrealon_sdk/src/clients/python_http/models/SuccessResponse.py +0 -25
- unrealon_sdk/src/clients/python_http/models/SystemNotificationResponse.py +0 -23
- unrealon_sdk/src/clients/python_http/models/ValidationError.py +0 -18
- unrealon_sdk/src/clients/python_http/models/ValidationErrorResponse.py +0 -21
- unrealon_sdk/src/clients/python_http/models/WebSocketMetrics.py +0 -21
- unrealon_sdk/src/clients/python_http/models/__init__.py +0 -44
- unrealon_sdk/src/clients/python_http/services/None_service.py +0 -35
- unrealon_sdk/src/clients/python_http/services/ParserManagement_service.py +0 -190
- unrealon_sdk/src/clients/python_http/services/ProxyManagement_service.py +0 -289
- unrealon_sdk/src/clients/python_http/services/SocketLogging_service.py +0 -187
- unrealon_sdk/src/clients/python_http/services/SystemHealth_service.py +0 -119
- unrealon_sdk/src/clients/python_http/services/WebSocketAPI_service.py +0 -198
- unrealon_sdk/src/clients/python_http/services/__init__.py +0 -0
- unrealon_sdk/src/clients/python_http/services/admin_service.py +0 -125
- unrealon_sdk/src/clients/python_http/services/async_None_service.py +0 -35
- unrealon_sdk/src/clients/python_http/services/async_ParserManagement_service.py +0 -190
- unrealon_sdk/src/clients/python_http/services/async_ProxyManagement_service.py +0 -289
- unrealon_sdk/src/clients/python_http/services/async_SocketLogging_service.py +0 -189
- unrealon_sdk/src/clients/python_http/services/async_SystemHealth_service.py +0 -123
- unrealon_sdk/src/clients/python_http/services/async_WebSocketAPI_service.py +0 -200
- unrealon_sdk/src/clients/python_http/services/async_admin_service.py +0 -125
- unrealon_sdk/src/clients/python_websocket/__init__.py +0 -28
- unrealon_sdk/src/clients/python_websocket/client.py +0 -490
- unrealon_sdk/src/clients/python_websocket/events.py +0 -732
- unrealon_sdk/src/clients/python_websocket/example.py +0 -136
- unrealon_sdk/src/clients/python_websocket/types.py +0 -871
- unrealon_sdk/src/core/__init__.py +0 -64
- unrealon_sdk/src/core/client.py +0 -556
- unrealon_sdk/src/core/config.py +0 -465
- unrealon_sdk/src/core/exceptions.py +0 -239
- unrealon_sdk/src/core/metadata.py +0 -191
- unrealon_sdk/src/core/models.py +0 -142
- unrealon_sdk/src/core/types.py +0 -68
- unrealon_sdk/src/dto/__init__.py +0 -268
- unrealon_sdk/src/dto/authentication.py +0 -108
- unrealon_sdk/src/dto/cache.py +0 -208
- unrealon_sdk/src/dto/common.py +0 -19
- unrealon_sdk/src/dto/concurrency.py +0 -393
- unrealon_sdk/src/dto/events.py +0 -108
- unrealon_sdk/src/dto/health.py +0 -339
- unrealon_sdk/src/dto/load_balancing.py +0 -336
- unrealon_sdk/src/dto/logging.py +0 -230
- unrealon_sdk/src/dto/performance.py +0 -165
- unrealon_sdk/src/dto/rate_limiting.py +0 -295
- unrealon_sdk/src/dto/resource_pooling.py +0 -128
- unrealon_sdk/src/dto/structured_logging.py +0 -112
- unrealon_sdk/src/dto/task_scheduling.py +0 -121
- unrealon_sdk/src/dto/websocket.py +0 -55
- unrealon_sdk/src/enterprise/__init__.py +0 -59
- unrealon_sdk/src/enterprise/authentication.py +0 -401
- unrealon_sdk/src/enterprise/cache_manager.py +0 -578
- unrealon_sdk/src/enterprise/error_recovery.py +0 -494
- unrealon_sdk/src/enterprise/event_system.py +0 -549
- unrealon_sdk/src/enterprise/health_monitor.py +0 -747
- unrealon_sdk/src/enterprise/load_balancer.py +0 -964
- unrealon_sdk/src/enterprise/logging/__init__.py +0 -68
- unrealon_sdk/src/enterprise/logging/cleanup.py +0 -156
- unrealon_sdk/src/enterprise/logging/development.py +0 -744
- unrealon_sdk/src/enterprise/logging/service.py +0 -410
- unrealon_sdk/src/enterprise/multithreading_manager.py +0 -853
- unrealon_sdk/src/enterprise/performance_monitor.py +0 -539
- unrealon_sdk/src/enterprise/proxy_manager.py +0 -696
- unrealon_sdk/src/enterprise/rate_limiter.py +0 -652
- unrealon_sdk/src/enterprise/resource_pool.py +0 -763
- unrealon_sdk/src/enterprise/task_scheduler.py +0 -709
- unrealon_sdk/src/internal/__init__.py +0 -10
- unrealon_sdk/src/internal/command_router.py +0 -497
- unrealon_sdk/src/internal/connection_manager.py +0 -397
- unrealon_sdk/src/internal/http_client.py +0 -446
- unrealon_sdk/src/internal/websocket_client.py +0 -420
- unrealon_sdk/src/provider.py +0 -471
- unrealon_sdk/src/utils.py +0 -234
- /unrealon_browser/{src/cli → cli}/__init__.py +0 -0
- /unrealon_browser/{src/cli → cli}/interactive_mode.py +0 -0
- /unrealon_browser/{src/cli → cli}/main.py +0 -0
- /unrealon_browser/{src/core → core}/__init__.py +0 -0
- /unrealon_browser/{src/dto → dto}/__init__.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/config.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/core.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/dataclasses.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/detection.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/enums.py +0 -0
- /unrealon_browser/{src/dto → dto}/models/statistics.py +0 -0
- /unrealon_browser/{src/managers → managers}/__init__.py +0 -0
- /unrealon_browser/{src/managers → managers}/stealth.py +0 -0
|
@@ -1,510 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
UnrealOn Driver v3.0 Simple CLI
|
|
3
|
-
|
|
4
|
-
Restored full Click-based CLI functionality from v2.0.
|
|
5
|
-
KISS principle - does exactly what you need with new Driver v3.0 architecture.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import asyncio
|
|
9
|
-
import signal
|
|
10
|
-
import time
|
|
11
|
-
from typing import Callable, Optional, Dict, Any
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
|
|
14
|
-
import click
|
|
15
|
-
|
|
16
|
-
from unrealon_driver.src.core.parser import Parser
|
|
17
|
-
from unrealon_driver.src.dto.cli import ParserInstanceConfig, create_parser_config
|
|
18
|
-
from unrealon_driver.src.config.auto_config import AutoConfig
|
|
19
|
-
from unrealon_driver.src.dto.config import LogLevel
|
|
20
|
-
from unrealon_driver.src.utils import TimeFormatter, ScheduleTimer, DaemonTimer
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class SimpleParser(Parser):
|
|
24
|
-
"""
|
|
25
|
-
Enhanced Parser with Click CLI integration.
|
|
26
|
-
|
|
27
|
-
Combines the revolutionary v3.0 Parser with the proven CLI system from v2.0.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
def __init__(self, config: ParserInstanceConfig):
|
|
31
|
-
"""
|
|
32
|
-
Initialize SimpleParser with Pydantic configuration.
|
|
33
|
-
|
|
34
|
-
Args:
|
|
35
|
-
config: ParserInstanceConfig with validated configuration
|
|
36
|
-
"""
|
|
37
|
-
if not isinstance(config, ParserInstanceConfig):
|
|
38
|
-
raise TypeError(f"Expected ParserInstanceConfig, got {type(config)}")
|
|
39
|
-
|
|
40
|
-
# Store validated config
|
|
41
|
-
self.cli_config = config
|
|
42
|
-
# Initialize base Parser with converted config
|
|
43
|
-
super().__init__(
|
|
44
|
-
parser_id=config.parser_id,
|
|
45
|
-
parser_name=config.parser_name,
|
|
46
|
-
config=config.to_parser_config(),
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
# Override _config after super().__init__() for logger compatibility
|
|
50
|
-
self._config = config
|
|
51
|
-
|
|
52
|
-
# Shutdown event for graceful stopping
|
|
53
|
-
self._shutdown_event = asyncio.Event()
|
|
54
|
-
|
|
55
|
-
if self.logger:
|
|
56
|
-
self.logger.info(f"🚀 SimpleParser initialized: {self.parser_name}")
|
|
57
|
-
|
|
58
|
-
async def setup(self) -> None:
|
|
59
|
-
"""Setup parser resources (override in subclass if needed)."""
|
|
60
|
-
if self.logger:
|
|
61
|
-
self.logger.info("Setting up parser resources...")
|
|
62
|
-
|
|
63
|
-
async def cleanup(self) -> None:
|
|
64
|
-
"""Cleanup parser resources (override in subclass if needed)."""
|
|
65
|
-
if self.logger:
|
|
66
|
-
self.logger.info("Cleaning up parser resources...")
|
|
67
|
-
await super().cleanup()
|
|
68
|
-
|
|
69
|
-
async def parse(self) -> dict:
|
|
70
|
-
"""
|
|
71
|
-
Main parsing method - override this in your parser.
|
|
72
|
-
Should return parsed data.
|
|
73
|
-
"""
|
|
74
|
-
return await self.parse_data()
|
|
75
|
-
|
|
76
|
-
async def parse_data(self) -> dict:
|
|
77
|
-
"""
|
|
78
|
-
Override this method in your parser.
|
|
79
|
-
Should return parsed data.
|
|
80
|
-
|
|
81
|
-
Note: This is an alias for parse() method for CLI compatibility.
|
|
82
|
-
"""
|
|
83
|
-
raise NotImplementedError(
|
|
84
|
-
f"Parser '{self.parser_name}' must implement either parse() or parse_data() method."
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
def create_click_cli(self, custom_commands: Optional[Dict[str, Callable]] = None):
|
|
88
|
-
"""
|
|
89
|
-
Create a Click CLI interface for this parser.
|
|
90
|
-
|
|
91
|
-
Args:
|
|
92
|
-
custom_commands: Dict of command_name -> async_function for custom commands
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
@click.group(invoke_without_command=True)
|
|
96
|
-
@click.pass_context
|
|
97
|
-
def cli(ctx):
|
|
98
|
-
"""Revolutionary parser CLI with zero configuration."""
|
|
99
|
-
if ctx.invoked_subcommand is None:
|
|
100
|
-
click.echo(f"🚀 {self.parser_name}")
|
|
101
|
-
click.echo(
|
|
102
|
-
f" {self.cli_config.description or 'Revolutionary web automation'}"
|
|
103
|
-
)
|
|
104
|
-
click.echo("\nAvailable commands:")
|
|
105
|
-
click.echo(" test - Run test mode")
|
|
106
|
-
click.echo(" daemon - Run daemon mode (WebSocket service)")
|
|
107
|
-
click.echo(" scheduled - Run scheduled mode")
|
|
108
|
-
click.echo(" interactive - Run interactive mode")
|
|
109
|
-
|
|
110
|
-
if custom_commands:
|
|
111
|
-
click.echo("\nCustom commands:")
|
|
112
|
-
for cmd_name in custom_commands.keys():
|
|
113
|
-
click.echo(f" {cmd_name}")
|
|
114
|
-
|
|
115
|
-
@cli.command()
|
|
116
|
-
@click.option("--verbose", "-v", is_flag=True, help="Verbose output")
|
|
117
|
-
@click.option("--show-browser", is_flag=True, help="Show browser window")
|
|
118
|
-
def test(verbose, show_browser):
|
|
119
|
-
"""Run parser in test mode."""
|
|
120
|
-
|
|
121
|
-
async def run():
|
|
122
|
-
start_time = time.time()
|
|
123
|
-
try:
|
|
124
|
-
click.echo(f"🧪 Starting {self.parser_name} test...")
|
|
125
|
-
click.echo(f"🕐 Started at: {TimeFormatter.format_datetime()}")
|
|
126
|
-
|
|
127
|
-
# Configure test options
|
|
128
|
-
test_config = {}
|
|
129
|
-
if verbose:
|
|
130
|
-
test_config["verbose"] = True
|
|
131
|
-
if show_browser:
|
|
132
|
-
test_config["show_browser"] = True
|
|
133
|
-
|
|
134
|
-
result = await self.test(**test_config)
|
|
135
|
-
|
|
136
|
-
duration = time.time() - start_time
|
|
137
|
-
|
|
138
|
-
if result["success"]:
|
|
139
|
-
click.echo(f"✅ Test completed successfully in {TimeFormatter.format_duration(duration)}!")
|
|
140
|
-
if result.get("data"):
|
|
141
|
-
count = (
|
|
142
|
-
len(result["data"])
|
|
143
|
-
if isinstance(result["data"], (list, dict))
|
|
144
|
-
else 1
|
|
145
|
-
)
|
|
146
|
-
click.echo(f"📊 Result: {count} items")
|
|
147
|
-
else:
|
|
148
|
-
click.echo(f"❌ Test failed after {TimeFormatter.format_duration(duration)}: {result['error']['message']}")
|
|
149
|
-
|
|
150
|
-
except Exception as e:
|
|
151
|
-
duration = time.time() - start_time
|
|
152
|
-
click.echo(f"❌ Test error after {TimeFormatter.format_duration(duration)}: {e}")
|
|
153
|
-
raise
|
|
154
|
-
|
|
155
|
-
try:
|
|
156
|
-
asyncio.run(run())
|
|
157
|
-
except RuntimeError as e:
|
|
158
|
-
if "cannot be called from a running event loop" in str(e):
|
|
159
|
-
# Already in event loop, run synchronously
|
|
160
|
-
|
|
161
|
-
loop = asyncio.get_event_loop()
|
|
162
|
-
if loop.is_running():
|
|
163
|
-
# Create task for running loop
|
|
164
|
-
task = loop.create_task(run())
|
|
165
|
-
# Don't await here as it would block
|
|
166
|
-
else:
|
|
167
|
-
asyncio.run(run())
|
|
168
|
-
else:
|
|
169
|
-
raise
|
|
170
|
-
|
|
171
|
-
@cli.command()
|
|
172
|
-
@click.option("--server", "-s", help="WebSocket server URL")
|
|
173
|
-
@click.option("--api-key", "-k", help="API key for authentication")
|
|
174
|
-
def daemon(server, api_key):
|
|
175
|
-
"""Run parser in daemon mode (WebSocket service)."""
|
|
176
|
-
|
|
177
|
-
async def run():
|
|
178
|
-
daemon_timer = DaemonTimer()
|
|
179
|
-
|
|
180
|
-
try:
|
|
181
|
-
click.echo(f"🔌 Starting {self.parser_name} daemon...")
|
|
182
|
-
click.echo(f"🕐 Started at: {TimeFormatter.format_datetime()}")
|
|
183
|
-
if server:
|
|
184
|
-
click.echo(f"🌐 Server: {server}")
|
|
185
|
-
if api_key:
|
|
186
|
-
click.echo("🔑 API key provided")
|
|
187
|
-
click.echo("Press Ctrl+C to stop\n")
|
|
188
|
-
|
|
189
|
-
kwargs = {}
|
|
190
|
-
if server:
|
|
191
|
-
kwargs["server"] = server
|
|
192
|
-
daemon_timer.connect()
|
|
193
|
-
if api_key:
|
|
194
|
-
kwargs["api_key"] = api_key
|
|
195
|
-
|
|
196
|
-
# Start daemon with periodic status updates
|
|
197
|
-
daemon_task = asyncio.create_task(self.daemon(**kwargs))
|
|
198
|
-
|
|
199
|
-
# Status update task
|
|
200
|
-
async def status_updates():
|
|
201
|
-
while not daemon_task.done():
|
|
202
|
-
await asyncio.sleep(30) # Every 30 seconds
|
|
203
|
-
daemon_timer.heartbeat()
|
|
204
|
-
click.echo(
|
|
205
|
-
f"💓 Status - Uptime: {daemon_timer.get_uptime()}"
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
status_task = asyncio.create_task(status_updates())
|
|
209
|
-
|
|
210
|
-
# Wait for completion
|
|
211
|
-
await daemon_task
|
|
212
|
-
status_task.cancel()
|
|
213
|
-
|
|
214
|
-
except KeyboardInterrupt:
|
|
215
|
-
click.echo(f"\n⏹️ Daemon stopped (Ctrl+C)")
|
|
216
|
-
click.echo(f"⏰ Final uptime: {daemon_timer.get_uptime()}")
|
|
217
|
-
except Exception as e:
|
|
218
|
-
click.echo(f"❌ Daemon error: {e}")
|
|
219
|
-
raise
|
|
220
|
-
|
|
221
|
-
try:
|
|
222
|
-
asyncio.run(run())
|
|
223
|
-
except RuntimeError as e:
|
|
224
|
-
if "cannot be called from a running event loop" in str(e):
|
|
225
|
-
# Already in event loop, run synchronously
|
|
226
|
-
|
|
227
|
-
loop = asyncio.get_event_loop()
|
|
228
|
-
if loop.is_running():
|
|
229
|
-
# Create task for running loop
|
|
230
|
-
task = loop.create_task(run())
|
|
231
|
-
# Don't await here as it would block
|
|
232
|
-
else:
|
|
233
|
-
asyncio.run(run())
|
|
234
|
-
else:
|
|
235
|
-
raise
|
|
236
|
-
|
|
237
|
-
@cli.command()
|
|
238
|
-
@click.option(
|
|
239
|
-
"--every",
|
|
240
|
-
"-e",
|
|
241
|
-
default="30m",
|
|
242
|
-
help="Execution interval (e.g., '30m', '1h', 'daily')",
|
|
243
|
-
)
|
|
244
|
-
@click.option(
|
|
245
|
-
"--at", "-a", help="Time for daily/weekly schedules (e.g., '09:00')"
|
|
246
|
-
)
|
|
247
|
-
@click.option("--max-runs", type=int, help="Maximum number of runs")
|
|
248
|
-
def scheduled(every, at, max_runs):
|
|
249
|
-
"""Run parser in scheduled mode."""
|
|
250
|
-
|
|
251
|
-
async def run():
|
|
252
|
-
schedule_timer = ScheduleTimer(every)
|
|
253
|
-
|
|
254
|
-
try:
|
|
255
|
-
click.echo(f"⏰ Starting {self.parser_name} scheduled mode...")
|
|
256
|
-
click.echo(f"🕐 Started at: {TimeFormatter.format_datetime()}")
|
|
257
|
-
click.echo(f"📅 Schedule: every {every}")
|
|
258
|
-
if at:
|
|
259
|
-
click.echo(f"🕘 At time: {at}")
|
|
260
|
-
if max_runs:
|
|
261
|
-
click.echo(f"🔢 Max runs: {max_runs}")
|
|
262
|
-
click.echo("Press Ctrl+C to stop\n")
|
|
263
|
-
|
|
264
|
-
kwargs = {"every": every}
|
|
265
|
-
if at:
|
|
266
|
-
kwargs["at"] = at
|
|
267
|
-
if max_runs:
|
|
268
|
-
kwargs["max_runs"] = max_runs
|
|
269
|
-
|
|
270
|
-
# Custom schedule implementation with timing
|
|
271
|
-
run_count = 0
|
|
272
|
-
success_count = 0
|
|
273
|
-
|
|
274
|
-
while True:
|
|
275
|
-
if max_runs and run_count >= max_runs:
|
|
276
|
-
click.echo(f"🏁 Reached maximum runs ({max_runs})")
|
|
277
|
-
break
|
|
278
|
-
|
|
279
|
-
# Start run timer
|
|
280
|
-
schedule_timer.start_run()
|
|
281
|
-
run_start = time.time()
|
|
282
|
-
|
|
283
|
-
click.echo(
|
|
284
|
-
f"🚀 Starting run #{run_count + 1} at {TimeFormatter.format_time()}"
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
try:
|
|
288
|
-
# Execute parser
|
|
289
|
-
result = await self.parse()
|
|
290
|
-
duration = time.time() - run_start
|
|
291
|
-
|
|
292
|
-
if result:
|
|
293
|
-
success_count += 1
|
|
294
|
-
click.echo(
|
|
295
|
-
f"✅ Run #{run_count + 1} completed in {TimeFormatter.format_duration(duration)}"
|
|
296
|
-
)
|
|
297
|
-
else:
|
|
298
|
-
click.echo(
|
|
299
|
-
f"❌ Run #{run_count + 1} failed after {TimeFormatter.format_duration(duration)}"
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
except Exception as e:
|
|
303
|
-
duration = time.time() - run_start
|
|
304
|
-
click.echo(
|
|
305
|
-
f"❌ Run #{run_count + 1} error after {TimeFormatter.format_duration(duration)}: {e}"
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
run_count += 1
|
|
309
|
-
|
|
310
|
-
# Show status
|
|
311
|
-
click.echo(
|
|
312
|
-
f"📊 Progress: {success_count}/{run_count} successful"
|
|
313
|
-
)
|
|
314
|
-
click.echo(
|
|
315
|
-
f"⏰ Total elapsed: {schedule_timer.get_elapsed_total()}"
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
if max_runs and run_count >= max_runs:
|
|
319
|
-
break
|
|
320
|
-
|
|
321
|
-
# Show next run info
|
|
322
|
-
click.echo(
|
|
323
|
-
f"🕐 Next run at: {TimeFormatter.format_time(schedule_timer.next_run)}"
|
|
324
|
-
)
|
|
325
|
-
click.echo(
|
|
326
|
-
f"⏳ Waiting: {schedule_timer.get_time_until_next()}"
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
# Sleep until next run with live countdown
|
|
330
|
-
sleep_seconds = TimeFormatter.parse_interval(every)
|
|
331
|
-
|
|
332
|
-
def countdown_callback(current_time, remaining_time, remaining_seconds, update_in_place=False):
|
|
333
|
-
if update_in_place:
|
|
334
|
-
# Live update on same line
|
|
335
|
-
import sys
|
|
336
|
-
sys.stdout.write(f"\r⏳ {current_time} - Time remaining: {remaining_time}")
|
|
337
|
-
sys.stdout.flush()
|
|
338
|
-
else:
|
|
339
|
-
click.echo(f"⏳ {current_time} - Time remaining: {remaining_time}")
|
|
340
|
-
|
|
341
|
-
await TimeFormatter.countdown_sleep(sleep_seconds, countdown_callback)
|
|
342
|
-
|
|
343
|
-
except KeyboardInterrupt:
|
|
344
|
-
click.echo(f"\n⏹️ Scheduled mode stopped (Ctrl+C)")
|
|
345
|
-
click.echo(
|
|
346
|
-
f"⏰ Total runtime: {schedule_timer.get_elapsed_total()}"
|
|
347
|
-
)
|
|
348
|
-
click.echo(f"🔄 Runs completed: {schedule_timer.run_count}")
|
|
349
|
-
except Exception as e:
|
|
350
|
-
click.echo(f"❌ Scheduled mode error: {e}")
|
|
351
|
-
raise
|
|
352
|
-
|
|
353
|
-
try:
|
|
354
|
-
asyncio.run(run())
|
|
355
|
-
except RuntimeError as e:
|
|
356
|
-
if "cannot be called from a running event loop" in str(e):
|
|
357
|
-
# Already in event loop, run synchronously
|
|
358
|
-
|
|
359
|
-
loop = asyncio.get_event_loop()
|
|
360
|
-
if loop.is_running():
|
|
361
|
-
# Create task for running loop
|
|
362
|
-
task = loop.create_task(run())
|
|
363
|
-
# Don't await here as it would block
|
|
364
|
-
else:
|
|
365
|
-
asyncio.run(run())
|
|
366
|
-
else:
|
|
367
|
-
raise
|
|
368
|
-
|
|
369
|
-
@cli.command()
|
|
370
|
-
def interactive():
|
|
371
|
-
"""Run parser in interactive mode."""
|
|
372
|
-
|
|
373
|
-
async def run():
|
|
374
|
-
start_time = time.time()
|
|
375
|
-
try:
|
|
376
|
-
click.echo(f"🎮 Starting {self.parser_name} interactive mode...")
|
|
377
|
-
click.echo(f"🕐 Started at: {TimeFormatter.format_datetime()}")
|
|
378
|
-
await self.interactive()
|
|
379
|
-
except KeyboardInterrupt:
|
|
380
|
-
duration = time.time() - start_time
|
|
381
|
-
click.echo(f"⏹️ Interactive mode stopped (Ctrl+C)")
|
|
382
|
-
click.echo(f"⏰ Session duration: {TimeFormatter.format_duration(duration)}")
|
|
383
|
-
except Exception as e:
|
|
384
|
-
duration = time.time() - start_time
|
|
385
|
-
click.echo(f"❌ Interactive mode error after {TimeFormatter.format_duration(duration)}: {e}")
|
|
386
|
-
raise
|
|
387
|
-
|
|
388
|
-
try:
|
|
389
|
-
asyncio.run(run())
|
|
390
|
-
except RuntimeError as e:
|
|
391
|
-
if "cannot be called from a running event loop" in str(e):
|
|
392
|
-
# Already in event loop, run synchronously
|
|
393
|
-
|
|
394
|
-
loop = asyncio.get_event_loop()
|
|
395
|
-
if loop.is_running():
|
|
396
|
-
# Create task for running loop
|
|
397
|
-
task = loop.create_task(run())
|
|
398
|
-
# Don't await here as it would block
|
|
399
|
-
else:
|
|
400
|
-
asyncio.run(run())
|
|
401
|
-
else:
|
|
402
|
-
raise
|
|
403
|
-
|
|
404
|
-
# Add custom commands
|
|
405
|
-
if custom_commands:
|
|
406
|
-
for cmd_name, cmd_func in custom_commands.items():
|
|
407
|
-
|
|
408
|
-
@cli.command(name=cmd_name)
|
|
409
|
-
@click.pass_context
|
|
410
|
-
def custom_cmd(ctx, _cmd_func=cmd_func):
|
|
411
|
-
"""Custom command."""
|
|
412
|
-
|
|
413
|
-
async def run():
|
|
414
|
-
try:
|
|
415
|
-
result = await _cmd_func(self)
|
|
416
|
-
if result:
|
|
417
|
-
click.echo("✅ Command completed!")
|
|
418
|
-
except Exception as e:
|
|
419
|
-
click.echo(f"❌ Command error: {e}")
|
|
420
|
-
raise
|
|
421
|
-
|
|
422
|
-
try:
|
|
423
|
-
asyncio.run(run())
|
|
424
|
-
except RuntimeError as e:
|
|
425
|
-
if "cannot be called from a running event loop" in str(e):
|
|
426
|
-
# Already in event loop, run synchronously
|
|
427
|
-
try:
|
|
428
|
-
loop = asyncio.get_event_loop()
|
|
429
|
-
if loop.is_running():
|
|
430
|
-
# Create task for running loop
|
|
431
|
-
task = loop.create_task(run())
|
|
432
|
-
# Don't await here as it would block
|
|
433
|
-
else:
|
|
434
|
-
asyncio.run(run())
|
|
435
|
-
except RuntimeError:
|
|
436
|
-
# No event loop, just skip
|
|
437
|
-
pass
|
|
438
|
-
else:
|
|
439
|
-
raise
|
|
440
|
-
|
|
441
|
-
return cli
|
|
442
|
-
|
|
443
|
-
# Legacy compatibility methods from v2.0
|
|
444
|
-
async def run_test(self) -> dict:
|
|
445
|
-
"""Legacy compatibility: run parser in test mode."""
|
|
446
|
-
result = await self.test()
|
|
447
|
-
if hasattr(result, "data") and result.data is not None:
|
|
448
|
-
return result.data
|
|
449
|
-
elif hasattr(result, "data") and isinstance(result.data, dict):
|
|
450
|
-
return result.data
|
|
451
|
-
elif isinstance(result, dict):
|
|
452
|
-
return result
|
|
453
|
-
else:
|
|
454
|
-
return {}
|
|
455
|
-
|
|
456
|
-
async def run_daemon(self) -> None:
|
|
457
|
-
"""Legacy compatibility: run parser in daemon mode."""
|
|
458
|
-
await self.daemon()
|
|
459
|
-
|
|
460
|
-
async def run_scheduled(self) -> None:
|
|
461
|
-
"""Legacy compatibility: run parser in scheduled mode."""
|
|
462
|
-
every = getattr(self, "scheduled_interval", "30m")
|
|
463
|
-
await self.schedule(every=str(every))
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
# Convenience function for quick Click CLI creation
|
|
467
|
-
def create_click_parser_cli(
|
|
468
|
-
parser_class: type,
|
|
469
|
-
parser_id: str,
|
|
470
|
-
parser_name: Optional[str] = None,
|
|
471
|
-
description: Optional[str] = None,
|
|
472
|
-
custom_commands: Optional[Dict[str, Callable]] = None,
|
|
473
|
-
**config_kwargs,
|
|
474
|
-
):
|
|
475
|
-
"""
|
|
476
|
-
Create a Click CLI for a parser class.
|
|
477
|
-
|
|
478
|
-
Args:
|
|
479
|
-
parser_class: Parser class (should inherit from SimpleParser)
|
|
480
|
-
parser_id: Unique parser identifier
|
|
481
|
-
parser_name: Human-readable name
|
|
482
|
-
description: Parser description
|
|
483
|
-
custom_commands: Dict of command_name -> async_function for custom commands
|
|
484
|
-
**config_kwargs: Additional config parameters
|
|
485
|
-
|
|
486
|
-
Returns:
|
|
487
|
-
Click CLI group ready to run
|
|
488
|
-
|
|
489
|
-
Usage:
|
|
490
|
-
if __name__ == "__main__":
|
|
491
|
-
cli = create_click_parser_cli(
|
|
492
|
-
MyParser,
|
|
493
|
-
"my_parser",
|
|
494
|
-
custom_commands={"custom": my_custom_func}
|
|
495
|
-
)
|
|
496
|
-
cli()
|
|
497
|
-
"""
|
|
498
|
-
# Create parser config
|
|
499
|
-
config = create_parser_config(
|
|
500
|
-
parser_id=parser_id,
|
|
501
|
-
parser_name=parser_name,
|
|
502
|
-
description=description,
|
|
503
|
-
**config_kwargs,
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
# Create parser instance
|
|
507
|
-
parser = parser_class(config)
|
|
508
|
-
|
|
509
|
-
# Create and return CLI
|
|
510
|
-
return parser.create_click_cli(custom_commands)
|