0
0
mirror of https://github.com/yeongpin/cursor-free-vip.git synced 2025-04-24 08:25:23 +00:00

Update version to 1.8.10, add user authorization check feature, and enhance localization support

- Bumped version in .env file to 1.8.10.
- Introduced a new script for checking user authorization with detailed feedback and error handling.
- Updated CHANGELOG.md to include new entries for the user authorization feature and minor fixes.
- Added localization strings for user authorization checks in English, Simplified Chinese, and Traditional Chinese.
This commit is contained in:
yeongpin 2025-04-09 20:49:39 +08:00
parent 491b227486
commit 3f9cbc3d08
8 changed files with 318 additions and 8 deletions

4
.env
View File

@ -1,2 +1,2 @@
version=1.8.09
VERSION=1.8.09
version=1.8.10
VERSION=1.8.10

View File

@ -1,5 +1,9 @@
# Change Log
## v1.8.10
1. Add: Check User Authorized | 添加檢查用戶授權
2. Fix: Some Issues | 修復一些問題
## v1.8.09
1. Add: Force Update Config | 添加強制更新配置
2. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持

214
check_user_authorized.py Normal file
View File

@ -0,0 +1,214 @@
import os
import requests
import time
import hashlib
import base64
import struct
from colorama import Fore, Style, init
# Initialize colorama
init()
# 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"""
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"""
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=None) -> str:
"""Generate Cursor checksum from token using the algorithm"""
try:
# 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:
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=None) -> bool:
"""
Check if the user is authorized with the given token
Args:
token (str): 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}")
# 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
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)
# 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 - this endpoint doesn't need a request body
usage_response = requests.post(
'https://api2.cursor.sh/aiserver.v1.DashboardService/GetUsageBasedPremiumRequests',
headers=headers,
data=b'', # Empty body
timeout=10
)
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:
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:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.user_unauthorized') if translator else 'User is unauthorized'}{Style.RESET_ALL}")
return False
else:
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:
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 Exception as 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:
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:
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:
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:
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 run(translator=None):
"""Run function to be called from main.py"""
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':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.getting_token_from_db') if translator else 'Getting token from database...'}{Style.RESET_ALL}")
try:
# 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:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.token_found_in_db') if translator else 'Token found in database'}{Style.RESET_ALL}")
else:
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}")
except ImportError:
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}")
except Exception as 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}")
# 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:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.authorization_successful') if translator else 'Authorization successful!'}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.authorization_failed') if translator else 'Authorization failed!'}{Style.RESET_ALL}")
return is_authorized
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.operation_cancelled') if translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
return False
def main(translator=None):
"""Main function to check user authorization"""
return run(translator)
if __name__ == "__main__":
main()

View File

@ -54,7 +54,7 @@ def setup_config(translator=None):
},
'Utils': {
'enabled_update_check': 'True',
'enabled_force_update': 'True',
'enabled_force_update': 'False',
'enabled_account_info': 'True'
}
}

View File

@ -30,7 +30,8 @@
"continue_prompt": "Continue? (y/N): ",
"operation_cancelled_by_user": "Operation cancelled by user",
"exiting": "Exiting ……",
"bypass_version_check": "Bypass Cursor Version Check"
"bypass_version_check": "Bypass Cursor Version Check",
"check_user_authorized": "Check User Authorized"
},
"languages": {
"en": "English",
@ -698,5 +699,33 @@
"title": "Cursor Version Bypass Tool",
"description": "This tool modifies Cursor's product.json to bypass version restrictions",
"menu_option": "Bypass Cursor Version Check"
},
"auth_check": {
"checking_authorization": "Checking authorization...",
"token_source": "Get token from database or input manually? (d/m, default: d)",
"getting_token_from_db": "Getting token from database...",
"token_found_in_db": "Token found in database",
"token_not_found_in_db": "Token not found in database",
"cursor_acc_info_not_found": "cursor_acc_info.py not found",
"error_getting_token_from_db": "Error getting token from database: {error}",
"enter_token": "Enter your Cursor token: ",
"token_length": "Token length: {length} characters",
"usage_response_status": "Usage response status: {response}",
"unexpected_status_code": "Unexpected status code: {code}",
"jwt_token_warning": "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.",
"invalid_token": "Invalid token",
"user_authorized": "User is authorized",
"user_unauthorized": "User is unauthorized",
"request_timeout": "Request timed out",
"connection_error": "Connection error",
"check_error": "Error checking authorization: {error}",
"authorization_successful": "Authorization successful!",
"authorization_failed": "Authorization failed!",
"operation_cancelled": "Operation cancelled by user",
"unexpected_error": "Unexpected error: {error}",
"error_generating_checksum": "Error generating checksum: {error}",
"checking_usage_information": "Checking usage information...",
"check_usage_response": "Check usage response: {response}",
"usage_response": "Usage response: {response}"
}
}

View File

@ -30,7 +30,8 @@
"continue_prompt": "继续?(y/N): ",
"operation_cancelled_by_user": "操作被用户取消",
"exiting": "退出中 ……",
"bypass_version_check": "绕过 Cursor 版本检查"
"bypass_version_check": "绕过 Cursor 版本检查",
"check_user_authorized": "检查用户授权"
},
"languages": {
"en": "英语",
@ -676,5 +677,33 @@
"title": "Cursor 版本绕过工具",
"description": "此工具修改 Cursor 的 product.json 以绕过版本限制",
"menu_option": "绕过 Cursor 版本检查"
},
"auth_check": {
"checking_authorization": "检查授权...",
"token_source": "从数据库获取 token 或手动输入d/m, 默认: d",
"getting_token_from_db": "从数据库获取 token...",
"token_found_in_db": "在数据库中找到 token",
"token_not_found_in_db": "在数据库中未找到 token",
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
"error_getting_token_from_db": "从数据库获取 token 时出错: {error}",
"enter_token": "请输入您的 Cursor token: ",
"token_length": "token 长度: {length}",
"usage_response_status": "使用情况响应状态: {response}",
"unexpected_status_code": "意外状态码: {code}",
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 检查返回意外状态码。token 可能有效但 API 访问受限。",
"invalid_token": "无效的 token",
"user_authorized": "用户已授权",
"user_unauthorized": "用户未授权",
"request_timeout": "请求超时",
"connection_error": "连接错误",
"check_error": "检查授权时出错: {error}",
"authorization_successful": "授权成功",
"authorization_failed": "授权失败",
"operation_cancelled": "操作已取消",
"unexpected_error": "意外错误: {error}",
"error_generating_checksum": "生成校验和时出错: {error}",
"checking_usage_information": "检查使用情况...",
"check_usage_response": "检查使用情况响应: {response}",
"usage_response": "使用情况响应: {response}"
}
}

View File

@ -30,7 +30,8 @@
"continue_prompt": "繼續?(y/N): ",
"operation_cancelled_by_user": "操作被使用者取消",
"exiting": "退出中 ……",
"bypass_version_check": "繞過 Cursor 版本檢查"
"bypass_version_check": "繞過 Cursor 版本檢查",
"check_user_authorized": "檢查用戶授權"
},
"languages": {
"en": "英文",
@ -658,5 +659,33 @@
"title": "Cursor 版本繞過工具",
"description": "此工具修改 Cursor 的 product.json 以繞過版本限制",
"menu_option": "繞過 Cursor 版本檢查"
},
"auth_check": {
"checking_authorization": "檢查授權...",
"token_source": "從資料庫獲取 token 或手動輸入d/m, 預設: d",
"getting_token_from_db": "從資料庫獲取 token...",
"token_found_in_db": "在資料庫中找到 token",
"token_not_found_in_db": "在資料庫中未找到 token",
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
"usage_response_status": "使用情況響應狀態: {response}",
"unexpected_status_code": "意外狀態碼: {code}",
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 檢查返回意外狀態碼。token 可能有效但 API 訪問受限。",
"error_getting_token_from_db": "從資料庫獲取 token 時出錯: {error}",
"enter_token": "請輸入您的 Cursor token: ",
"token_length": "token 長度: {length}",
"invalid_token": "無效的 token",
"user_authorized": "用戶已授權",
"user_unauthorized": "用戶未授權",
"request_timeout": "請求超時",
"connection_error": "連接錯誤",
"check_error": "檢查授權時出錯: {error}",
"authorization_successful": "授權成功",
"authorization_failed": "授權失敗",
"operation_cancelled": "操作已取消",
"unexpected_error": "意外錯誤: {error}",
"error_generating_checksum": "生成校驗和時出錯: {error}",
"checking_usage_information": "檢查使用情況...",
"check_usage_response": "檢查使用情況響應: {response}",
"usage_response": "使用情況響應: {response}"
}
}

View File

@ -287,7 +287,8 @@ def print_menu():
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}",
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account', fallback='Delete Cursor Google Account')}",
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}"
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}",
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized', fallback='Check User Authorized')}"
}
# Automatically calculate the number of menu items in the left and right columns
@ -560,7 +561,7 @@ def main():
while True:
try:
choice_num = 15
choice_num = 16
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
if choice == "0":
@ -630,6 +631,10 @@ def main():
import bypass_version
bypass_version.main(translator)
print_menu()
elif choice == "16":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()