Files
cursor-free-vip-main/utils.py
XnsYT fe445cc298 feat: Add enhanced configuration, error handling and utility systems v1.11.04
## 🚀 Enhanced Features Implementation

This PR introduces significant improvements to the Cursor Free VIP project with enhanced configuration management, error handling, and utility systems.

###  New Features

#### 🔧 Enhanced Configuration Management (`enhanced_config.py`)
- **Multi-format support**: INI, JSON, YAML configuration formats
- **Automatic validation**: Built-in configuration validation with detailed error reporting
- **Backup system**: Automatic configuration backup and restore functionality
- **Platform-specific paths**: Automatic detection and management of paths for Windows, macOS, and Linux
- **Type safety**: Improved type checking and error handling

#### 🛡️ Enhanced Error Handling (`enhanced_error_handler.py`)
- **Automatic categorization**: Intelligent error classification (Network, File System, Process, etc.)
- **Severity-based logging**: Critical, High, Medium, Low severity levels
- **Recovery strategies**: Automatic retry logic with exponential backoff
- **Error history**: Comprehensive error tracking and resolution management
- **Custom callbacks**: Registerable error handlers for specific categories

#### 🛠️ Enhanced Utility System (`enhanced_utils.py`)
- **Advanced path management**: Cross-platform path detection and validation
- **Multi-browser support**: Automatic detection of Chrome, Firefox, Edge, Brave, Opera
- **Process monitoring**: Real-time process tracking and management
- **System information**: Detailed system resource monitoring
- **Network connectivity**: Automated network testing and validation

### 🔧 Technical Improvements

- **Type safety**: Fixed all linter errors and type annotation issues
- **Code robustness**: Improved error handling and exception management
- **Maintainability**: Better code organization and documentation
- **Cross-platform compatibility**: Enhanced support for Windows, macOS, and Linux

### �� Files Changed

- `enhanced_config.py` - New enhanced configuration management system
- `enhanced_utils.py` - New enhanced utility and system management
- `enhanced_error_handler.py` - New enhanced error handling system
- `CHANGELOG.md` - Updated with v1.11.04 release notes

### �� Testing

- All new systems have been tested on Windows, macOS, and Linux
- Type checking passes with no linter errors
- Backward compatibility maintained with existing functionality

### 📝 Documentation

- Comprehensive inline documentation for all new features
- Updated CHANGELOG with detailed feature descriptions
- Follows existing code style and conventions

---

**Note**: These enhancements provide a solid foundation for future development while maintaining full backward compatibility with existing functionality.
2025-07-10 18:46:02 +02:00

333 lines
13 KiB
Python

import os
import sys
import platform
import random
import shutil
import logging
from typing import Optional, Dict, List, Union, Tuple
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger(__name__)
def get_user_documents_path() -> str:
"""Get user documents path across different operating systems.
Returns:
str: Path to user's Documents directory
"""
if platform.system() == "Windows":
try:
import winreg
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders") as key:
documents_path, _ = winreg.QueryValueEx(key, "Personal")
return documents_path
except Exception as e:
logger.warning(f"Failed to get Documents path from registry: {e}")
return os.path.expanduser("~\\Documents")
elif platform.system() == "Darwin": # macOS
return os.path.expanduser("~/Documents")
else: # Linux and other Unix-like systems
# Check for XDG user directories
try:
with open(os.path.expanduser("~/.config/user-dirs.dirs"), "r") as f:
for line in f:
if line.startswith("XDG_DOCUMENTS_DIR"):
path = line.split("=")[1].strip().strip('"').replace("$HOME", os.path.expanduser("~"))
if os.path.exists(path):
return path
except (FileNotFoundError, IOError):
pass
# Fallback to ~/Documents
return os.path.expanduser("~/Documents")
def find_executable(executable_names: List[str]) -> Optional[str]:
"""Find executable in PATH by trying multiple possible names.
Args:
executable_names: List of possible executable names to try
Returns:
Path to the executable if found, None otherwise
"""
for name in executable_names:
try:
path = shutil.which(name)
if path:
return path
except Exception:
continue
return None
def get_default_driver_path(browser_type: str = 'chrome') -> str:
"""Get default driver path based on browser type.
Args:
browser_type: Type of browser ('chrome', 'edge', 'firefox', 'brave')
Returns:
str: Path to the browser driver
"""
browser_type = browser_type.lower()
driver_map = {
'chrome': get_default_chrome_driver_path,
'edge': get_default_edge_driver_path,
'firefox': get_default_firefox_driver_path,
'brave': get_default_chrome_driver_path, # Brave uses Chrome driver
'opera': get_default_chrome_driver_path, # Opera uses Chrome driver
'operagx': get_default_chrome_driver_path # OperaGX uses Chrome driver
}
driver_func = driver_map.get(browser_type, get_default_chrome_driver_path)
return driver_func()
def get_default_chrome_driver_path() -> str:
"""Get default Chrome driver path based on platform."""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver")
else: # Linux and other Unix-like systems
# Try to find chromedriver in PATH first
path = find_executable(["chromedriver"])
if path:
return path
return "/usr/local/bin/chromedriver"
def get_default_edge_driver_path() -> str:
"""Get default Edge driver path based on platform."""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver")
else: # Linux and other Unix-like systems
path = find_executable(["msedgedriver"])
if path:
return path
return "/usr/local/bin/msedgedriver"
def get_default_firefox_driver_path() -> str:
"""Get default Firefox driver path based on platform."""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver")
else: # Linux and other Unix-like systems
path = find_executable(["geckodriver"])
if path:
return path
return "/usr/local/bin/geckodriver"
def get_default_browser_path(browser_type: str = 'chrome') -> str:
"""Get default browser executable path based on platform and browser type.
Args:
browser_type: Type of browser ('chrome', 'edge', 'firefox', 'brave', 'opera', 'operagx')
Returns:
str: Path to the browser executable
"""
browser_type = browser_type.lower()
# Platform-specific browser paths
if sys.platform == "win32":
return _get_windows_browser_path(browser_type)
elif sys.platform == "darwin":
return _get_macos_browser_path(browser_type)
else: # Linux and other Unix-like systems
return _get_linux_browser_path(browser_type)
def _get_windows_browser_path(browser_type: str) -> str:
"""Get browser path for Windows."""
browser_paths = {
'chrome': [
shutil.which("chrome"),
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'Application', 'chrome.exe')
],
'edge': [
shutil.which("msedge"),
r"C:\Program Files\Microsoft\Edge\Application\msedge.exe",
r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
],
'firefox': [
shutil.which("firefox"),
r"C:\Program Files\Mozilla Firefox\firefox.exe",
r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
],
'opera': [
shutil.which("opera"),
r"C:\Program Files\Opera\opera.exe",
r"C:\Program Files (x86)\Opera\opera.exe",
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe')
],
'operagx': [
shutil.which("opera"),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe'),
r"C:\Program Files\Opera GX\opera.exe",
r"C:\Program Files (x86)\Opera GX\opera.exe"
],
'brave': [
shutil.which("brave"),
os.path.join(os.environ.get('PROGRAMFILES', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe')
]
}
# Return first existing path
paths = browser_paths.get(browser_type, browser_paths['chrome'])
for path in paths:
if path and os.path.exists(path):
return path
# Return first path as fallback
return next((p for p in paths if p), r"C:\Program Files\Google\Chrome\Application\chrome.exe")
def _get_macos_browser_path(browser_type: str) -> str:
"""Get browser path for macOS."""
browser_paths = {
'chrome': [
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
],
'edge': [
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
"~/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
],
'firefox': [
"/Applications/Firefox.app/Contents/MacOS/firefox",
"~/Applications/Firefox.app/Contents/MacOS/firefox"
],
'brave': [
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
"~/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
],
'opera': [
"/Applications/Opera.app/Contents/MacOS/Opera",
"~/Applications/Opera.app/Contents/MacOS/Opera"
],
'operagx': [
"/Applications/Opera GX.app/Contents/MacOS/Opera",
"~/Applications/Opera GX.app/Contents/MacOS/Opera"
]
}
# Return first existing path
paths = browser_paths.get(browser_type, browser_paths['chrome'])
for path in paths:
expanded_path = os.path.expanduser(path)
if os.path.exists(expanded_path):
return expanded_path
# Return first path as fallback
return os.path.expanduser(paths[0])
def _get_linux_browser_path(browser_type: str) -> str:
"""Get browser path for Linux."""
browser_executables = {
'chrome': ["google-chrome", "chrome", "chromium", "chromium-browser"],
'edge': ["microsoft-edge", "msedge"],
'firefox': ["firefox", "firefox-esr"],
'opera': ["opera"],
'operagx': ["opera-gx", "opera"],
'brave': ["brave-browser", "brave"]
}
# Try to find executable in PATH
executables = browser_executables.get(browser_type, browser_executables['chrome'])
path = find_executable(executables)
if path:
return path
# Fallback to common locations
common_locations = {
'chrome': "/usr/bin/google-chrome",
'edge': "/usr/bin/microsoft-edge",
'firefox': "/usr/bin/firefox",
'opera': "/usr/bin/opera",
'operagx': "/usr/bin/opera",
'brave': "/usr/bin/brave-browser"
}
return common_locations.get(browser_type, common_locations['chrome'])
def get_linux_cursor_path() -> str:
"""Get Linux Cursor path by checking multiple possible locations."""
possible_paths = [
"/opt/Cursor/resources/app",
"/usr/share/cursor/resources/app",
"/opt/cursor-bin/resources/app",
"/usr/lib/cursor/resources/app",
os.path.expanduser("~/.local/share/cursor/resources/app"),
# Add extracted AppImage paths
*[p for p in [os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app")] if os.path.exists(p)]
]
# Return first existing path or default if none exists
for path in possible_paths:
if os.path.exists(path):
return path
# Log warning if no path found
logger.warning("No Cursor installation found in common Linux paths")
return possible_paths[0]
def parse_time_range(time_str: str) -> Tuple[float, float]:
"""Parse a time range string into min and max values.
Args:
time_str: String representing time range (e.g., "0.5-1.5" or "0.5,1.5")
Returns:
Tuple of (min_time, max_time)
"""
try:
if isinstance(time_str, (int, float)):
return float(time_str), float(time_str)
if '-' in time_str:
min_time, max_time = map(float, time_str.split('-'))
elif ',' in time_str:
min_time, max_time = map(float, time_str.split(','))
else:
min_time = max_time = float(time_str)
return min_time, max_time
except (ValueError, TypeError):
return 0.5, 1.5
def get_random_wait_time(config: Dict, timing_key: str, default_range: Tuple[float, float] = (0.5, 1.5)) -> float:
"""Get random wait time based on configuration timing settings.
Args:
config: Configuration dictionary containing timing settings
timing_key: Key to look up in the timing settings
default_range: Default time range to use if config value is invalid
Returns:
float: Random wait time in seconds
"""
try:
# Get timing value from config
if not config or 'Timing' not in config:
return random.uniform(*default_range)
timing = config.get('Timing', {}).get(timing_key)
if not timing:
return random.uniform(*default_range)
min_time, max_time = parse_time_range(timing)
return random.uniform(min_time, max_time)
except Exception as e:
logger.warning(f"Error getting wait time for {timing_key}: {e}")
return random.uniform(*default_range)