Files
cursor-free-vip-main/check_user_authorized.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

366 lines
17 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import requests
import time
import hashlib
import base64
import struct
import logging
from colorama import Fore, Style, init
from typing import Optional, Dict, Union, Any, Tuple
# Initialize colorama
init(autoreset=True)
# 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__)
# Define emoji constants
EMOJI = {
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"WARNING": "⚠️",
"KEY": "🔑",
"CHECK": "🔍"
}
def generate_hashed64_hex(input_str: str, salt: str = '') -> str:
"""Generate a SHA-256 hash of input + salt and return as hex.
Args:
input_str: The input string to hash
salt: Optional salt to add to the input string
Returns:
str: Hexadecimal representation of the hash
"""
if not input_str:
logger.warning("Empty input string provided for hashing")
return ""
hash_obj = hashlib.sha256()
hash_obj.update((input_str + salt).encode('utf-8'))
return hash_obj.hexdigest()
def obfuscate_bytes(byte_array: bytearray) -> bytearray:
"""Obfuscate bytes using the algorithm from utils.js.
Args:
byte_array: The byte array to obfuscate
Returns:
bytearray: The obfuscated byte array
"""
if not byte_array:
return bytearray()
t = 165
for r in range(len(byte_array)):
byte_array[r] = ((byte_array[r] ^ t) + (r % 256)) & 0xFF
t = byte_array[r]
return byte_array
def generate_cursor_checksum(token: str, translator: Any = None) -> str:
"""Generate Cursor checksum from token using the algorithm.
Args:
token: The authentication token
translator: Optional translator for internationalization
Returns:
str: The generated checksum
"""
try:
# Validate input
if not token or not isinstance(token, str):
logger.error("Invalid token provided")
return ""
# Clean the token
clean_token = token.strip()
# Generate machineId and macMachineId
machine_id = generate_hashed64_hex(clean_token, 'machineId')
mac_machine_id = generate_hashed64_hex(clean_token, 'macMachineId')
# Get timestamp and convert to byte array
timestamp = int(time.time() * 1000) // 1000000
byte_array = bytearray(struct.pack('>Q', timestamp)[-6:]) # Take last 6 bytes
# Obfuscate bytes and encode as base64
obfuscated_bytes = obfuscate_bytes(byte_array)
encoded_checksum = base64.b64encode(obfuscated_bytes).decode('utf-8')
# Combine final checksum
return f"{encoded_checksum}{machine_id}/{mac_machine_id}"
except Exception as e:
logger.error(f"Error generating checksum: {e}")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.error_generating_checksum', error=str(e)) if translator else f'Error generating checksum: {str(e)}'}{Style.RESET_ALL}")
return ""
def check_user_authorized(token: str, translator: Any = None) -> bool:
"""
Check if the user is authorized with the given token.
Args:
token: The authorization token
translator: Optional translator for internationalization
Returns:
bool: True if authorized, False otherwise
"""
try:
print(f"{Fore.CYAN}{EMOJI['CHECK']} {translator.get('auth_check.checking_authorization') if translator else 'Checking authorization...'}{Style.RESET_ALL}")
# Validate input
if not token or not isinstance(token, str):
logger.error("Invalid token provided")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.invalid_token') if translator else 'Invalid token'}{Style.RESET_ALL}")
return False
# Clean the token
if token and '%3A%3A' in token:
token = token.split('%3A%3A')[1]
elif token and '::' in token:
token = token.split('::')[1]
# Remove any whitespace
token = token.strip()
if not token or len(token) < 10: # Add a basic validation for token length
logger.error("Token too short or empty after cleaning")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.invalid_token') if translator else 'Invalid token'}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_length', length=len(token)) if translator else f'Token length: {len(token)} characters'}{Style.RESET_ALL}")
# Try to get usage info using the DashboardService API
try:
# Generate checksum
checksum = generate_cursor_checksum(token, translator)
if not checksum:
logger.error("Failed to generate checksum")
return False
# Create request headers
headers = {
'accept-encoding': 'gzip',
'authorization': f'Bearer {token}',
'connect-protocol-version': '1',
'content-type': 'application/proto',
'user-agent': 'connect-es/1.6.1',
'x-cursor-checksum': checksum,
'x-cursor-client-version': '0.48.7',
'x-cursor-timezone': 'Asia/Shanghai',
'x-ghost-mode': 'false',
'Host': 'api2.cursor.sh'
}
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.checking_usage_information') if translator else 'Checking usage information...'}{Style.RESET_ALL}")
# Make the request with timeout and retry
max_retries = 3
for attempt in range(max_retries):
try:
usage_response = requests.post(
'https://api2.cursor.sh/aiserver.v1.DashboardService/GetUsageBasedPremiumRequests',
headers=headers,
data=b'', # Empty body
timeout=10
)
break
except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
if attempt < max_retries - 1:
logger.warning(f"Request attempt {attempt + 1} failed: {e}. Retrying...")
time.sleep(2)
else:
raise
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.usage_response', response=usage_response.status_code) if translator else f'Usage response status: {usage_response.status_code}'}{Style.RESET_ALL}")
if usage_response.status_code == 200:
logger.info("User is authorized")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.user_authorized') if translator else 'User is authorized'}{Style.RESET_ALL}")
return True
elif usage_response.status_code == 401 or usage_response.status_code == 403:
logger.warning("User is unauthorized")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.user_unauthorized') if translator else 'User is unauthorized'}{Style.RESET_ALL}")
return False
else:
logger.warning(f"Unexpected status code: {usage_response.status_code}")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.unexpected_status_code', code=usage_response.status_code) if translator else f'Unexpected status code: {usage_response.status_code}'}{Style.RESET_ALL}")
# If the token at least looks like a valid JWT, consider it valid
if token.startswith('eyJ') and '.' in token and len(token) > 100:
logger.info("Token appears to be in JWT format, but API check returned an unexpected status code")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
return True
return False
except requests.exceptions.Timeout:
logger.error("Request timed out")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.request_timeout') if translator else 'Request timed out'}{Style.RESET_ALL}")
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
if token.startswith('eyJ') and '.' in token and len(token) > 100:
logger.info("Token appears to be in JWT format, but request timed out")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check timed out. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
return True
return False
except requests.exceptions.ConnectionError:
logger.error("Connection error")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.connection_error') if translator else 'Connection error'}{Style.RESET_ALL}")
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
if token.startswith('eyJ') and '.' in token and len(token) > 100:
logger.info("Token appears to be in JWT format, but connection failed")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API connection failed. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
return True
return False
except Exception as e:
logger.error(f"Error checking usage: {e}")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error checking usage: {str(e)}{Style.RESET_ALL}")
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
if token.startswith('eyJ') and '.' in token and len(token) > 100:
logger.info("Token appears to be in JWT format, but API check failed")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check failed. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
return True
return False
except requests.exceptions.Timeout:
logger.error("Request timed out")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.request_timeout') if translator else 'Request timed out'}{Style.RESET_ALL}")
return False
except requests.exceptions.ConnectionError:
logger.error("Connection error")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.connection_error') if translator else 'Connection error'}{Style.RESET_ALL}")
return False
except Exception as e:
logger.error(f"Error checking authorization: {e}")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.check_error', error=str(e)) if translator else f'Error checking authorization: {str(e)}'}{Style.RESET_ALL}")
return False
def get_token_from_database(translator: Any = None) -> str:
"""
Get token from database using cursor_acc_info.py.
Args:
translator: Optional translator for internationalization
Returns:
str: The token if found, empty string otherwise
"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.getting_token_from_db') if translator else 'Getting token from database...'}{Style.RESET_ALL}")
# Import functions from cursor_acc_info.py
from cursor_acc_info import get_token
# Get token using the get_token function
token = get_token()
if token:
logger.info("Token found in database")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.token_found_in_db') if translator else 'Token found in database'}{Style.RESET_ALL}")
return token
else:
logger.warning("Token not found in database")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.token_not_found_in_db') if translator else 'Token not found in database'}{Style.RESET_ALL}")
return ""
except ImportError:
logger.error("cursor_acc_info.py not found")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.cursor_acc_info_not_found') if translator else 'cursor_acc_info.py not found'}{Style.RESET_ALL}")
return ""
except Exception as e:
logger.error(f"Error getting token from database: {e}")
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.error_getting_token_from_db', error=str(e)) if translator else f'Error getting token from database: {str(e)}'}{Style.RESET_ALL}")
return ""
def run(translator: Any = None) -> bool:
"""Run function to be called from main.py.
Args:
translator: Optional translator for internationalization
Returns:
bool: True if authorization successful, False otherwise
"""
try:
# Ask user if they want to get token from database or input manually
choice = input(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_source') if translator else 'Get token from database or input manually? (d/m, default: d): '}{Style.RESET_ALL}").strip().lower()
token = None
# If user chooses database or default
if not choice or choice == 'd':
token = get_token_from_database(translator)
# If token not found in database or user chooses manual input
if not token:
# Try to get token from environment
token = os.environ.get('CURSOR_TOKEN')
# If not in environment, ask user to input
if not token:
token = input(f"{Fore.CYAN}{EMOJI['KEY']} {translator.get('auth_check.enter_token') if translator else 'Enter your Cursor token: '}{Style.RESET_ALL}")
# Check authorization
is_authorized = check_user_authorized(token, translator)
if is_authorized:
logger.info("Authorization successful")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.authorization_successful') if translator else 'Authorization successful!'}{Style.RESET_ALL}")
else:
logger.warning("Authorization failed")
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.authorization_failed') if translator else 'Authorization failed!'}{Style.RESET_ALL}")
return is_authorized
except Exception as e:
logger.error(f"Error in run function: {e}")
print(f"{Fore.RED}{EMOJI['ERROR']} An unexpected error occurred: {str(e)}{Style.RESET_ALL}")
return False
def main(translator: Any = None) -> bool:
"""Main function to be called when script is run directly.
Args:
translator: Optional translator for internationalization
Returns:
bool: True if authorization successful, False otherwise
"""
try:
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['CHECK']} {translator.get('auth_check.title') if translator else 'Check User Authorization'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
result = run(translator)
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('auth_check.press_enter') if translator else 'Press Enter to continue...'}")
return result
except Exception as e:
logger.error(f"Error in main function: {e}")
print(f"{Fore.RED}{EMOJI['ERROR']} An unexpected error occurred: {str(e)}{Style.RESET_ALL}")
return False
if __name__ == "__main__":
try:
from main import translator as main_translator
main(main_translator)
except ImportError:
logger.warning("Failed to import translator from main.py, running without translation")
main(None)
except Exception as e:
logger.error(f"Error running check_user_authorized.py: {e}")
print(f"{Fore.RED}{EMOJI['ERROR']} An unexpected error occurred: {str(e)}{Style.RESET_ALL}")