Compare commits

...

30 Commits

Author SHA1 Message Date
yeongpin
cce3025f7f feat: Enhance Name Generation and Improve Account Registration Process
- Implement realistic name generation with predefined name lists
- Modify first name letter for uniqueness
- Add more descriptive console output with emojis and translations
- Update localization files with new registration-related keys
- Optimize random name generation in registration modules
2025-03-03 11:58:28 +08:00
Pin Studios
fe3e27561b Update .env 2025-03-03 11:17:35 +08:00
yeongpin
bf2bea71eb Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-03-03 11:16:45 +08:00
yeongpin
77a61647dd docs: Update CHANGELOG with version 1.5.01 release notes 2025-03-03 11:10:10 +08:00
yeongpin
813dd4431e feat: Add version update checker with GitHub release support 2025-03-03 11:09:11 +08:00
Pin Studios
d7116b8cf3 Update .env 2025-03-02 13:55:25 +08:00
Pin Studios
f5a7acc4e3 Merge pull request #122 from bingoohuang/main
print account info as early as possible
2025-03-02 13:54:58 +08:00
bingoohuang
479933844a print account info as early as possible 2025-02-28 21:20:12 +08:00
yeongpin
93046d7f03 Update README.md with improved image styling and new screenshot 2025-02-27 10:44:18 +08:00
Pin Studios
e7ca31b710 Update build.yml 2025-02-27 10:33:53 +08:00
Pin Studios
87b99b0d16 Update CHANGELOG.md 2025-02-27 10:29:10 +08:00
Pin Studios
e983b6f560 Update .env 2025-02-27 10:27:18 +08:00
Pin Studios
21ca7ab24f Merge pull request #111 from ahmed98Osama/feature/return-to-menu
Return to main menu after each operation
2025-02-27 10:27:00 +08:00
Ahmed Osama
e7468644a4 feat: return to main menu after each operation instead of exiting 2025-02-26 19:07:14 +02:00
Pin Studios
2bca7d7d14 Update install.sh 2025-02-26 22:34:38 +08:00
Pin Studios
040c5f5836 Update install.ps1 2025-02-26 22:30:56 +08:00
Pin Studios
4a86bbeeb4 Update block_domain.txt 2025-02-26 22:01:53 +08:00
Pin Studios
68b7888a83 Merge pull request #101 from aliensb/main
add support for linux
2025-02-25 23:56:29 +08:00
yeongpin
78dee025a7 feat: Enhance Temporary Email Service with Domain Blocking and Improved Error Handling
- Add domain blocking mechanism to filter out problematic email domains
- Implement dynamic domain list retrieval from external source
- Improve error handling and logging in email creation process
- Add fallback service switching when domains are blocked
- Update localization files with new error and blocking-related messages
- Increment version to 1.4.06
2025-02-25 23:53:55 +08:00
Pin Studios
17c2b4b243 Create block_domain.txt 2025-02-25 23:30:53 +08:00
tom
2acb271f19 add support for linux 2025-02-25 16:52:32 +08:00
Pin Studios
b688b67c26 Update .gitignore 2025-02-25 12:23:45 +08:00
yeongpin
3543615a69 fix: Improve Windows Language Detection and Platform Compatibility
- Correct Windows language detection method in Translator class
- Update platform system check for Windows-specific imports
- Refactor language detection to use platform-specific checks
- Ensure more robust cross-platform language detection logic
2025-02-25 12:16:55 +08:00
yeongpin
24b0a5c09e refactor: Streamline System Language Detection Logic
- Consolidate language detection methods into a single unified approach
- Improve cross-platform language detection for Windows and Unix-like systems
- Simplify error handling and add more robust language code mapping
- Remove platform-specific detection methods in favor of a more generic approach
- Enhance language fallback mechanism with better default handling
2025-02-25 12:14:54 +08:00
yeongpin
5bbba05f69 feat: Improve System Language Detection in Translator Class
- Refactor language detection method with separate methods for Windows, macOS, and Linux
- Add more robust language detection for different platforms
- Improve error handling and fallback mechanisms
- Ensure consistent language code formatting (zh_CN, zh_TW)
- Update Windows language detection using windll with additional error checks
2025-02-25 12:09:13 +08:00
yeongpin
72c95e4b4f refactor: Simplify OS Detection and Download Process
- Streamline OS detection logic by removing detailed architecture checks
- Reduce complexity in download URL generation
- Remove verbose download verification steps
- Simplify error handling during binary download
2025-02-25 12:00:59 +08:00
yeongpin
8afd5df4ea fix: Correct Syntax Error in Installation Script Conditional Logic
- Fix incorrect brace placement in install_cursor_free_vip function
- Remove unnecessary closing brace that was causing syntax error
- Ensure proper flow control in installation script
2025-02-25 11:55:38 +08:00
yeongpin
a7a97b5621 feat: Enhance OS Detection and Download Resilience in Installation Script
- Add Windows system detection
- Implement fallback mechanism for macOS binary downloads
- Improve download link verification with HEAD request
- Add graceful handling for missing architecture-specific binaries
- Provide more informative error messages during download process
2025-02-25 11:54:45 +08:00
yeongpin
e2e2ebc12e feat: Enhance Download Verification in Installation Script
- Add verbose curl download logging
- Implement file size check for downloaded binaries
- Provide detailed error messages for download failures
- Improve download error handling and diagnostics
2025-02-25 11:53:37 +08:00
yeongpin
d131bccac0 feat: Improve macOS Architecture Detection in Installation Script
- Add detailed macOS architecture detection (ARM64 and Intel)
- Enhance system type logging with informative messages
- Provide more precise OS and architecture identification
2025-02-25 11:50:39 +08:00
18 changed files with 652 additions and 198 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.4.04 version=1.5.02
VERSION=1.4.04 VERSION=1.5.02

View File

@@ -6,7 +6,7 @@ on:
version: version:
description: 'Version number (e.g. 1.0.9)' description: 'Version number (e.g. 1.0.9)'
required: true required: true
default: '1.4.01' default: '1.4.07'
permissions: permissions:
contents: write contents: write
@@ -206,4 +206,4 @@ jobs:
draft: false draft: false
prerelease: false prerelease: false
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

49
.gitignore vendored
View File

@@ -1,8 +1,47 @@
cursor_accounts.txt __pycache__
/venv server/
/__pycache__ venv/
dist check_license.py
build cursor_modifier.py
reset_machine.py
Run_Venv.bat
token_monitor.py
get_mac.py
.gitignore
build.bat
build.mac.command
build.py
build.sh
ENV/
install.bat install.bat
run.bat run.bat
temp_account_info.txt temp_account_info.txt
.env copy
# PyInstaller
build/
dist/
*.spec2
credentials.txt
cursor_accounts.txt
recaptcha.py
install_requirements.bat
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Project specific
*.log
*.db
*.sqlite3

View File

@@ -1,5 +1,39 @@
# Change Log # Change Log
## v1.5.02
1. Add: Generate Random Name Alias | 增加生成隨機真實姓名
2. Add: Realistic Name Input | 增加真實姓名輸入
3. Optimize: Error Handling | 優化錯誤處理
4. Optimize: Translation | 優化翻譯
5. Optimize: Performance | 優化性能
## v1.5.01
1. Add: Check Latest Version | 增加檢查最新版本
2. Add: Update Command | 增加更新命令
## v1.4.08
1. Add: Print Some Account Info | 增加打印一些賬號信息
## v1.4.07
1. Add Removed break statements after each operation | 修改結束event後的break暫停應用
2. Added print_menu() calls to show the menu again | 添加print_menu調用以再次顯示菜單
3. Updated error handling to show menu instead of exiting | 更新錯誤處理以顯示菜單而不是退出
## v1.4.06
1. Add: Blocked Domains Loaded | 增加被屏蔽的域名加載
2. Fix: Cleanup Error | 修復清理進程時出錯
3. Fix: Blocked Domains Loaded Error | 修復被屏蔽的域名加載錯誤
4. Fix: Available Domains Loaded Error | 修復可用域名加載錯誤
5. Fix: Domains Filtered Error | 修復過濾後剩餘域名錯誤
6. Fix: Domains Excluded Error | 修復排除域名錯誤
## v1.4.05
1. Fix: macOS Language Detection | 修復macOS語言檢測
## v1.4.04 ## v1.4.04
1. Change Some Language Info to English | 更改一些語言信息為英文 1. Change Some Language Info to English | 更改一些語言信息為英文
@@ -184,4 +218,4 @@
<p align="center"> <p align="center">
<img src="./images/pro_2025-01-11_16-24-03.png" alt="Cursor Pro Logo" width="400"/><br> <img src="./images/pro_2025-01-11_16-24-03.png" alt="Cursor Pro Logo" width="400"/><br>
</p> </p>

View File

@@ -1,7 +1,7 @@
# ➤ Cursor Free VIP # ➤ Cursor Free VIP
<div align="center"> <div align="center">
<p align="center"> <p align="center">
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200"/> <img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
</p> </p>
<p align="center"> <p align="center">
@@ -19,7 +19,7 @@ This is a tool to automatically register , support Windows and macOS systems, co
這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。 這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。
<p align="center"> <p align="center">
<img src="./images/pronew_2025-02-13_15-01-32.png" alt="new" width="400"/><br> <img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br>
</p> </p>
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/) ##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)

3
block_domain.txt Normal file
View File

@@ -0,0 +1,3 @@
oakon.com
famamail.com
2925.com

View File

@@ -45,65 +45,6 @@ class BrowserControl:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.create_new_tab_failed', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.create_new_tab_failed', error=str(e))}{Style.RESET_ALL}")
return None return None
def switch_to_tab(self, browser):
"""切换到指定浏览器窗口"""
try:
self.browser = browser
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.switch_tab_success')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.switch_tab_failed', error=str(e))}{Style.RESET_ALL}")
return False
def get_current_tab(self):
"""获取当前标签页"""
return self.browser
def wait_for_page_load(self, seconds=2):
"""等待页面加载"""
time.sleep(seconds)
def navigate_to(self, url):
"""导航到指定URL"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.navigate_to', url=url)}...{Style.RESET_ALL}")
self.browser.get(url)
self.wait_for_page_load()
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.browser_error', error=str(e))}{Style.RESET_ALL}")
return False
def get_verification_code(self):
"""从邮件中获取验证码"""
try:
# 尝试所有可能的样式组合
selectors = [
# 新样式
'xpath://div[contains(@style, "font-family:-apple-system") and contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "color:#202020")]',
# 带行高的样式
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "line-height:30px")]',
# rgba 颜色样式
'xpath://div[contains(@style, "font-size: 28px") and contains(@style, "letter-spacing: 2px") and contains(@style, "color: rgba(32, 32, 32, 1)")]',
# 宽松样式
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]'
]
# 依次尝试每个选择器
for selector in selectors:
code_div = self.browser.ele(selector)
if code_div:
verification_code = code_div.text.strip()
if verification_code.isdigit() and len(verification_code) == 6:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.found_verification_code')}: {verification_code}{Style.RESET_ALL}")
return verification_code
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.no_valid_verification_code')}{Style.RESET_ALL}")
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_verification_code_error', error=str(e))}{Style.RESET_ALL}")
return None
def fill_verification_code(self, code): def fill_verification_code(self, code):
"""填写验证码""" """填写验证码"""

View File

@@ -26,6 +26,10 @@ class CursorAuth:
self.db_path = os.path.join( self.db_path = os.path.join(
os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb" os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb"
) )
elif os.name =='posix':
self.db_path = os.path.expanduser(
"~/.config/Cursor/User/globalStorage/state.vscdb"
)
else: # macOS else: # macOS
self.db_path = os.path.expanduser( self.db_path = os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb" "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"

View File

@@ -10,10 +10,10 @@ from reset_machine_manual import MachineIDResetter
os.environ["PYTHONVERBOSE"] = "0" os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0" os.environ["PYINSTALLER_VERBOSE"] = "0"
# 初始化colorama # Initialize colorama
init() init()
# 定义emoji常量 # Define emoji constants
EMOJI = { EMOJI = {
'START': '🚀', 'START': '🚀',
'FORM': '📝', 'FORM': '📝',
@@ -33,7 +33,7 @@ EMOJI = {
class CursorRegistration: class CursorRegistration:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
# 设置为显示模式 # Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False' os.environ['BROWSER_HEADLESS'] = 'False'
self.browser_manager = BrowserManager() self.browser_manager = BrowserManager()
self.browser = None self.browser = None
@@ -47,38 +47,49 @@ class CursorRegistration:
# 账号信息 # 账号信息
self.password = self._generate_password() self.password = self._generate_password()
self.first_name = self._generate_name() # Generate first name and last name separately
self.last_name = self._generate_name() first_name = random.choice([
"James", "John", "Robert", "Michael", "William", "David", "Joseph", "Thomas",
"Emma", "Olivia", "Ava", "Isabella", "Sophia", "Mia", "Charlotte", "Amelia",
"Liam", "Noah", "Oliver", "Elijah", "Lucas", "Mason", "Logan", "Alexander"
])
self.last_name = random.choice([
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Anderson", "Wilson", "Taylor", "Thomas", "Moore", "Martin", "Jackson", "Lee",
"Thompson", "White", "Harris", "Clark", "Lewis", "Walker", "Hall", "Young"
])
# Modify first letter of first name
new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
self.first_name = new_first_letter + first_name[1:]
print(f"\n{Fore.CYAN}{EMOJI['PASSWORD']} {self.translator.get('register.password')}: {self.password} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.first_name')}: {self.first_name} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.last_name')}: {self.last_name} {Style.RESET_ALL}")
def _generate_password(self, length=12): def _generate_password(self, length=12):
"""Generate Random Password""" """Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*" chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length)) return ''.join(random.choices(chars, k=length))
def _generate_name(self, length=6):
"""Generate Random Name"""
first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
rest_letters = ''.join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length-1))
return first_letter + rest_letters
def setup_email(self): def setup_email(self):
"""设置邮箱""" """Setup Email"""
try: try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}")
# 使用 new_tempemail 创建临时邮箱,传入 translator # Create a temporary email using new_tempemail, passing translator
from new_tempemail import NewTempEmail from new_tempemail import NewTempEmail
self.temp_email = NewTempEmail(self.translator) # 传入 translator self.temp_email = NewTempEmail(self.translator) # Pass translator
# 创建临时邮箱 # Create a temporary email
email_address = self.temp_email.create_email() email_address = self.temp_email.create_email()
if not email_address: if not email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_create_failed')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_create_failed')}{Style.RESET_ALL}")
return False return False
# 保存邮箱地址 # Save email address
self.email_address = email_address self.email_address = email_address
self.email_tab = self.temp_email # 传递 NewTempEmail 实例 self.email_tab = self.temp_email # Pass NewTempEmail instance
return True return True
@@ -92,10 +103,10 @@ class CursorRegistration:
try: try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
# 直接使用 new_signup.py 进行注册 # Directly use new_signup.py to sign up
from new_signup import main as new_signup_main from new_signup import main as new_signup_main
# 执行新的注册流程,传入 translator # Execute the new registration process, passing translator
result, browser_tab = new_signup_main( result, browser_tab = new_signup_main(
email=self.email_address, email=self.email_address,
password=self.password, password=self.password,
@@ -107,11 +118,11 @@ class CursorRegistration:
) )
if result: if result:
# 使用返回的浏览器实例获取账户信息 # Use the returned browser instance to get account information
self.signup_tab = browser_tab # 保存浏览器实例 self.signup_tab = browser_tab # Save browser instance
success = self._get_account_info() success = self._get_account_info()
# 获取信息后关闭浏览器 # Close browser after getting information
if browser_tab: if browser_tab:
try: try:
browser_tab.quit() browser_tab.quit()
@@ -126,7 +137,7 @@ class CursorRegistration:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
return False return False
finally: finally:
# 确保在任何情况下都关闭浏览器 # Ensure browser is closed in any case
if browser_tab: if browser_tab:
try: try:
browser_tab.quit() browser_tab.quit()
@@ -134,7 +145,7 @@ class CursorRegistration:
pass pass
def _get_account_info(self): def _get_account_info(self):
"""获取账户信息和 Token""" """Get Account Information and Token"""
try: try:
self.signup_tab.get(self.settings_url) self.signup_tab.get(self.settings_url)
time.sleep(2) time.sleep(2)
@@ -149,6 +160,7 @@ class CursorRegistration:
if usage_ele: if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip() total_usage = usage_ele.text.split("/")[-1].strip()
print(f"Total Usage: {total_usage}\n")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30 max_attempts = 30
retry_interval = 2 retry_interval = 2
@@ -185,7 +197,7 @@ class CursorRegistration:
return False return False
def _save_account_info(self, token, total_usage): def _save_account_info(self, token, total_usage):
"""保存账户信息到文件""" """Save Account Information to File"""
try: try:
# 先更新认证信息 # 先更新认证信息
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
@@ -200,7 +212,7 @@ class CursorRegistration:
if not resetter.reset_machine_ids(): # 直接调用reset_machine_ids方法 if not resetter.reset_machine_ids(): # 直接调用reset_machine_ids方法
raise Exception("Failed to reset machine ID") raise Exception("Failed to reset machine ID")
# 保存账户信息到文件 # Save account information to file
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f: with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n") f.write(f"\n{'='*50}\n")
f.write(f"Email: {self.email_address}\n") f.write(f"Email: {self.email_address}\n")
@@ -217,7 +229,7 @@ class CursorRegistration:
return False return False
def start(self): def start(self):
"""启动注册流程""" """Start Registration Process"""
try: try:
if self.setup_email(): if self.setup_email():
if self.register_cursor(): if self.register_cursor():
@@ -225,7 +237,7 @@ class CursorRegistration:
return True return True
return False return False
finally: finally:
# 关闭邮箱标签页 # Close email tab
if hasattr(self, 'temp_email'): if hasattr(self, 'temp_email'):
try: try:
self.temp_email.close() self.temp_email.close()
@@ -233,7 +245,7 @@ class CursorRegistration:
pass pass
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None): def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
"""更新Cursor的认证信息的便捷函数""" """Update Cursor Auth Info"""
auth_manager = CursorAuth(translator=self.translator) auth_manager = CursorAuth(translator=self.translator)
return auth_manager.update_auth(email, access_token, refresh_token) return auth_manager.update_auth(email, access_token, refresh_token)

View File

@@ -44,22 +44,33 @@ class CursorRegistration:
self.signup_tab = None self.signup_tab = None
self.email_tab = None self.email_tab = None
# Account information # Generate account information
self.password = self._generate_password() self.password = self._generate_password()
self.first_name = self._generate_name() # Generate first name and last name separately
self.last_name = self._generate_name() first_name = random.choice([
"James", "John", "Robert", "Michael", "William", "David", "Joseph", "Thomas",
"Emma", "Olivia", "Ava", "Isabella", "Sophia", "Mia", "Charlotte", "Amelia",
"Liam", "Noah", "Oliver", "Elijah", "Lucas", "Mason", "Logan", "Alexander"
])
self.last_name = random.choice([
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Anderson", "Wilson", "Taylor", "Thomas", "Moore", "Martin", "Jackson", "Lee",
"Thompson", "White", "Harris", "Clark", "Lewis", "Walker", "Hall", "Young"
])
# Modify first letter of first name
new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
self.first_name = new_first_letter + first_name[1:]
print(f"\n{Fore.CYAN}{EMOJI['PASSWORD']} {self.translator.get('register.password')}: {self.password} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.first_name')}: {self.first_name} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.last_name')}: {self.last_name} {Style.RESET_ALL}")
def _generate_password(self, length=12): def _generate_password(self, length=12):
"""Generate Random Password""" """Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*" chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length)) return ''.join(random.choices(chars, k=length))
def _generate_name(self, length=6):
"""Generate Random Name"""
first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
rest_letters = ''.join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length-1))
return first_letter + rest_letters
def setup_email(self): def setup_email(self):
"""Setup Email""" """Setup Email"""
try: try:
@@ -70,6 +81,7 @@ class CursorRegistration:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}\n{Style.RESET_ALL}")
return True return True
except Exception as e: except Exception as e:
@@ -155,6 +167,7 @@ class CursorRegistration:
if usage_ele: if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip() total_usage = usage_ele.text.split("/")[-1].strip()
print(f"Total Usage: {total_usage}\n")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30 max_attempts = 30
retry_interval = 2 retry_interval = 2

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -131,7 +131,12 @@
"register_process_error": "Register Process Error: {error}", "register_process_error": "Register Process Error: {error}",
"setting_password": "Setting Password", "setting_password": "Setting Password",
"manual_code_input": "Manual Code Input", "manual_code_input": "Manual Code Input",
"manual_email_input": "Manual Email Input" "manual_email_input": "Manual Email Input",
"password": "Password",
"first_name": "First Name",
"last_name": "Last Name",
"exit_signal": "Exit Signal",
"email_address": "Email Address"
}, },
"auth": { "auth": {
"title": "Cursor Auth Manager", "title": "Cursor Auth Manager",
@@ -203,7 +208,23 @@
"verification_code_found": "Verification Code Found", "verification_code_found": "Verification Code Found",
"verification_code_not_found": "Verification Code Not Found", "verification_code_not_found": "Verification Code Not Found",
"verification_code_error": "Verification Code Error: {error}", "verification_code_error": "Verification Code Error: {error}",
"address": "Email Address" "address": "Email Address",
"all_domains_blocked": "All Domains Blocked, Switching Service",
"no_available_domains_after_filtering": "No Available Domains After Filtering",
"switching_service": "Switching to {service} Service",
"domains_list_error": "Failed to Get Domains List: {error}",
"failed_to_get_available_domains": "Failed to Get Available Domains",
"domains_excluded": "Domains Excluded: {domains}",
"failed_to_create_account": "Failed to Create Account",
"account_creation_error": "Account Creation Error: {error}",
"blocked_domains": "Blocked Domains: {domains}",
"blocked_domains_loaded": "Blocked Domains Loaded: {count}",
"blocked_domains_loaded_error": "Blocked Domains Loaded Error: {error}",
"blocked_domains_loaded_success": "Blocked Domains Loaded Successfully",
"blocked_domains_loaded_timeout": "Blocked Domains Loaded Timeout: {timeout}s",
"blocked_domains_loaded_timeout_error": "Blocked Domains Loaded Timeout Error: {error}",
"available_domains_loaded": "Available Domains Loaded: {count}",
"domains_filtered": "Domains Filtered: {count}"
}, },
"update": { "update": {
"title": "Disable Cursor Auto Update", "title": "Disable Cursor Auto Update",
@@ -217,5 +238,13 @@
"directory_removed": "Directory Removed", "directory_removed": "Directory Removed",
"creating_block_file": "Creating Block File", "creating_block_file": "Creating Block File",
"block_file_created": "Block File Created" "block_file_created": "Block File Created"
},
"updater": {
"checking": "Checking for updates...",
"new_version_available": "New version available! (Current: {current}, Latest: {latest})",
"updating": "Updating to the latest version. The program will restart automatically.",
"up_to_date": "You are using the latest version.",
"check_failed": "Failed to check for updates: {error}",
"continue_anyway": "Continuing with current version..."
} }
} }

View File

@@ -131,7 +131,12 @@
"update_cursor_auth_info": "更新Cursor认证信息", "update_cursor_auth_info": "更新Cursor认证信息",
"setting_password": "设置密码", "setting_password": "设置密码",
"manual_code_input": "手动输入验证码", "manual_code_input": "手动输入验证码",
"manual_email_input": "手动输入邮箱" "manual_email_input": "手动输入邮箱",
"password": "密码",
"first_name": "名字",
"last_name": "姓氏",
"exit_signal": "退出信号",
"email_address": "邮箱地址"
}, },
"auth": { "auth": {
"title": "Cursor 认证管理器", "title": "Cursor 认证管理器",
@@ -200,7 +205,23 @@
"verification_code_found": "找到验证码", "verification_code_found": "找到验证码",
"verification_code_not_found": "未找到验证码", "verification_code_not_found": "未找到验证码",
"verification_code_error": "验证码错误: {error}", "verification_code_error": "验证码错误: {error}",
"address": "邮箱地址" "address": "邮箱地址",
"all_domains_blocked": "所有域名都被屏蔽了,切换服务",
"no_available_domains_after_filtering": "过滤后没有可用域名",
"switching_service": "切换到 {service} 服务",
"domains_list_error": "获取域名列表失败: {error}",
"failed_to_get_available_domains": "获取可用域名失败",
"blocked_domains_loaded": "加载了 {count} 个被屏蔽的域名",
"domains_excluded": "排除了 {domains} 个被屏蔽的域名",
"failed_to_create_account": "创建账户失败",
"account_creation_error": "账户创建错误: {error}",
"blocked_domains": "被屏蔽的域名: {domains}",
"blocked_domains_loaded_error": "加载被屏蔽的域名失败: {error}",
"blocked_domains_loaded_success": "加载被屏蔽的域名成功",
"blocked_domains_loaded_timeout": "加载被屏蔽的域名超时: {timeout}秒",
"blocked_domains_loaded_timeout_error": "加载被屏蔽的域名超时错误: {error}",
"available_domains_loaded": "获取到 {count} 个可用域名",
"domains_filtered": "过滤后剩餘 {count} 個可用域名"
}, },
"update": { "update": {
"title": "禁用 Cursor 自动更新", "title": "禁用 Cursor 自动更新",
@@ -214,5 +235,13 @@
"directory_removed": "目录已删除", "directory_removed": "目录已删除",
"creating_block_file": "创建阻止文件", "creating_block_file": "创建阻止文件",
"block_file_created": "阻止文件已创建" "block_file_created": "阻止文件已创建"
},
"updater": {
"checking": "检查更新...",
"new_version_available": "有新版本可用! (当前版本: {current}, 最新版本: {latest})",
"updating": "正在更新到最新版本。程序将自动重启。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "检查更新失败: {error}",
"continue_anyway": "继续使用当前版本..."
} }
} }

View File

@@ -112,7 +112,12 @@
"update_cursor_auth_info": "更新Cursor認證信息", "update_cursor_auth_info": "更新Cursor認證信息",
"setting_password": "設置密碼", "setting_password": "設置密碼",
"manual_code_input": "手動輸入驗證碼", "manual_code_input": "手動輸入驗證碼",
"manual_email_input": "手動輸入郵箱地址" "manual_email_input": "手動輸入郵箱地址",
"password": "密碼",
"first_name": "名字",
"last_name": "姓氏",
"exit_signal": "退出信號",
"email_address": "郵箱地址"
}, },
"auth": { "auth": {
"title": "Cursor 認證管理器", "title": "Cursor 認證管理器",
@@ -181,7 +186,23 @@
"verification_code_found": "找到驗證碼", "verification_code_found": "找到驗證碼",
"verification_code_not_found": "未找到驗證碼", "verification_code_not_found": "未找到驗證碼",
"verification_code_error": "驗證碼錯誤: {error}", "verification_code_error": "驗證碼錯誤: {error}",
"address": "郵箱地址" "address": "郵箱地址",
"all_domains_blocked": "所有域名都被屏蔽了,切換服務",
"no_available_domains_after_filtering": "過濾後沒有可用域名",
"switching_service": "切換到 {service} 服務",
"domains_list_error": "獲取域名列表失敗: {error}",
"failed_to_get_available_domains": "獲取可用域名失敗",
"domains_excluded": "排除的域名: {domains}",
"failed_to_create_account": "創建帳戶失敗",
"account_creation_error": "帳戶創建錯誤: {error}",
"blocked_domains": "被屏蔽的域名: {domains}",
"blocked_domains_loaded": "加載被屏蔽的域名: {domains}",
"blocked_domains_loaded_error": "加載被屏蔽的域名失敗: {error}",
"blocked_domains_loaded_success": "加載被屏蔽的域名成功",
"blocked_domains_loaded_timeout": "加載被屏蔽的域名超時: {timeout}秒",
"blocked_domains_loaded_timeout_error": "加載被屏蔽的域名超時錯誤: {error}",
"available_domains_loaded": "獲取到 {count} 個可用域名",
"domains_filtered": "過濾後剩餘 {count} 個可用域名"
}, },
"update": { "update": {
"title": "禁用 Cursor 自动更新", "title": "禁用 Cursor 自动更新",
@@ -195,5 +216,13 @@
"directory_removed": "目錄已刪除", "directory_removed": "目錄已刪除",
"creating_block_file": "創建阻止文件", "creating_block_file": "創建阻止文件",
"block_file_created": "阻止文件已創建" "block_file_created": "阻止文件已創建"
},
"updater": {
"checking": "檢查更新...",
"new_version_available": "有新版本可用! (當前版本: {current}, 最新版本: {latest})",
"updating": "正在更新到最新版本。程序將自動重啟。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "檢查更新失敗: {error}",
"continue_anyway": "繼續使用當前版本..."
} }
} }

68
main.py
View File

@@ -3,12 +3,18 @@
import os import os
import sys import sys
import json import json
from logo import print_logo from logo import print_logo, version
from colorama import Fore, Style, init from colorama import Fore, Style, init
import ctypes
from ctypes import windll
import locale import locale
import platform import platform
import requests
import subprocess
# 只在 Windows 系统上导入 windll
if platform.system() == 'Windows':
import ctypes
# 只在 Windows 上导入 windll
from ctypes import windll
# 初始化colorama # 初始化colorama
init() init()
@@ -29,8 +35,8 @@ EMOJI = {
class Translator: class Translator:
def __init__(self): def __init__(self):
self.current_language = self.detect_system_language() # Changed to use system language
self.translations = {} self.translations = {}
self.current_language = self.detect_system_language() # 使用正确的方法名
self.fallback_language = 'en' # Fallback language if translation is missing self.fallback_language = 'en' # Fallback language if translation is missing
self.load_translations() self.load_translations()
@@ -51,8 +57,12 @@ class Translator:
def _detect_windows_language(self): def _detect_windows_language(self):
"""Detect language on Windows systems""" """Detect language on Windows systems"""
try: try:
# Get the keyboard layout # 确保我们在 Windows 上
user32 = ctypes.WinDLL('user32', use_last_error=True) if platform.system() != 'Windows':
return 'en'
# 获取键盘布局
user32 = ctypes.windll.user32
hwnd = user32.GetForegroundWindow() hwnd = user32.GetForegroundWindow()
threadid = user32.GetWindowThreadProcessId(hwnd, 0) threadid = user32.GetWindowThreadProcessId(hwnd, 0)
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
@@ -195,8 +205,39 @@ def select_language():
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
return False return False
def check_latest_version():
"""Check if current version matches the latest release version"""
try:
print(f"\n{Fore.CYAN}{EMOJI['UPDATE']} {translator.get('updater.checking')}{Style.RESET_ALL}")
# Get latest version from GitHub API with timeout
response = requests.get("https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest", timeout=5)
latest_version = response.json()["tag_name"].lstrip('v')
if latest_version != version:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.new_version_available', current=version, latest=latest_version)}{Style.RESET_ALL}")
# Execute update command based on platform
if platform.system() == 'Windows':
update_command = 'irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex'
subprocess.run(['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', update_command], check=True)
else:
update_command = 'curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh'
subprocess.Popen(update_command, shell=True)
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.updating')}{Style.RESET_ALL}")
sys.exit(0)
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.check_failed', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.continue_anyway')}{Style.RESET_ALL}")
return # Continue with the program instead of blocking
def main(): def main():
print_logo() print_logo()
check_latest_version() # Add version check before showing menu
print_menu() print_menu()
while True: while True:
@@ -210,19 +251,19 @@ def main():
elif choice == "1": elif choice == "1":
import reset_machine_manual import reset_machine_manual
reset_machine_manual.run(translator) reset_machine_manual.run(translator)
break print_menu()
elif choice == "2": elif choice == "2":
import cursor_register import cursor_register
cursor_register.main(translator) cursor_register.main(translator)
break print_menu()
elif choice == "3": elif choice == "3":
import cursor_register_manual import cursor_register_manual
cursor_register_manual.main(translator) cursor_register_manual.main(translator)
break print_menu()
elif choice == "4": elif choice == "4":
import quit_cursor import quit_cursor
quit_cursor.quit_cursor(translator) quit_cursor.quit_cursor(translator)
break print_menu()
elif choice == "5": elif choice == "5":
if select_language(): if select_language():
print_menu() print_menu()
@@ -230,7 +271,7 @@ def main():
elif choice == "6": elif choice == "6":
import disable_auto_update import disable_auto_update
disable_auto_update.run(translator) disable_auto_update.run(translator)
break print_menu()
else: else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu() print_menu()
@@ -241,10 +282,7 @@ def main():
return return
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.error_occurred', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.error_occurred', error=str(e))}{Style.RESET_ALL}")
break print_menu()
print(f"\n{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('menu.press_enter')}...{Style.RESET_ALL}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -23,6 +23,47 @@ class NewTempEmail:
self.token = None self.token = None
self.email = None self.email = None
self.password = None self.password = None
self.blocked_domains = self.get_blocked_domains()
def get_blocked_domains(self):
"""Get blocked domains list"""
try:
block_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/block_domain.txt"
response = requests.get(block_url, timeout=5)
if response.status_code == 200:
# Split text and remove empty lines
domains = [line.strip() for line in response.text.split('\n') if line.strip()]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 已加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
return domains
return []
except Exception as e:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 获取被屏蔽域名列表失败: {str(e)}{Style.RESET_ALL}")
return []
def exclude_blocked_domains(self, domains):
"""Exclude blocked domains"""
if not self.blocked_domains:
return domains
filtered_domains = []
for domain in domains:
if domain['domain'] not in self.blocked_domains:
filtered_domains.append(domain)
excluded_count = len(domains) - len(filtered_domains)
if excluded_count > 0:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domains_excluded', domains=excluded_count)}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 已排除 {excluded_count} 个被屏蔽的域名{Style.RESET_ALL}")
return filtered_domains
def _generate_credentials(self): def _generate_credentials(self):
"""生成随机用户名和密码""" """生成随机用户名和密码"""
@@ -39,42 +80,112 @@ class NewTempEmail:
print(f"{Fore.CYAN} 正在访问 {self.selected_service['name']}...{Style.RESET_ALL}") print(f"{Fore.CYAN} 正在访问 {self.selected_service['name']}...{Style.RESET_ALL}")
# 获取可用域名列表 # 获取可用域名列表
domains_response = requests.get(f"{self.api_url}/domains") try:
if domains_response.status_code != 200: domains_response = requests.get(f"{self.api_url}/domains", timeout=10)
raise Exception(f"{self.translator.get('email.failed_to_get_available_domains')}") if domains_response.status_code != 200:
print(f"{Fore.RED}{self.translator.get('email.domains_list_error', error=domains_response.status_code)}{Style.RESET_ALL}")
print(f"{Fore.RED}{self.translator.get('email.domains_list_error', error=domains_response.text)}{Style.RESET_ALL}")
raise Exception(f"{self.translator.get('email.failed_to_get_available_domains') if self.translator else 'Failed to get available domains'}")
domains = domains_response.json()["hydra:member"]
print(f"{Fore.CYAN} {self.translator.get('email.available_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
domains = domains_response.json()["hydra:member"] if not domains:
if not domains: raise Exception(f"{self.translator.get('email.no_available_domains') if self.translator else '没有可用域名'}")
raise Exception(f"{self.translator.get('email.no_available_domains')}") except Exception as e:
print(f"{Fore.RED}❌ 获取域名列表时出错: {str(e)}{Style.RESET_ALL}")
raise
# 排除被屏蔽的域名
try:
filtered_domains = self.exclude_blocked_domains(domains)
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.domains_filtered', count=len(filtered_domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 过滤后剩余 {len(filtered_domains)} 个可用域名{Style.RESET_ALL}")
if not filtered_domains:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.all_domains_blocked')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 所有域名都被屏蔽了,尝试切换服务{Style.RESET_ALL}")
# 切换到另一个服务
for service in self.services:
if service["api_url"] != self.api_url:
self.selected_service = service
self.api_url = service["api_url"]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.switching_service', service=service['name'])}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 切换到 {service['name']} 服务{Style.RESET_ALL}")
return self.create_email() # 递归调用
raise Exception(f"{self.translator.get('email.no_available_domains_after_filtering') if self.translator else '过滤后没有可用域名'}")
except Exception as e:
print(f"{Fore.RED}❌ 过滤域名时出错: {str(e)}{Style.RESET_ALL}")
raise
# 生成随机用户名和密码 # 生成随机用户名和密码
username, password = self._generate_credentials() try:
self.password = password username, password = self._generate_credentials()
self.password = password
# 创建邮箱账户
selected_domain = filtered_domains[0]['domain']
email = f"{username}@{selected_domain}"
print(f"{Fore.CYAN} 尝试创建邮箱: {email}{Style.RESET_ALL}")
account_data = {
"address": email,
"password": password
}
except Exception as e:
print(f"{Fore.RED}❌ 生成凭据时出错: {str(e)}{Style.RESET_ALL}")
raise
# 创建邮箱账户 # 创建账户
email = f"{username}@{domains[0]['domain']}" try:
account_data = { create_response = requests.post(f"{self.api_url}/accounts", json=account_data, timeout=15)
"address": email,
"password": password if create_response.status_code != 201:
} print(f"{Fore.RED}❌ 创建账户失败: 状态码 {create_response.status_code}{Style.RESET_ALL}")
print(f"{Fore.RED}❌ 响应内容: {create_response.text}{Style.RESET_ALL}")
create_response = requests.post(f"{self.api_url}/accounts", json=account_data)
if create_response.status_code != 201: # 如果是域名问题,尝试下一个域名
raise Exception(f"{self.translator.get('email.failed_to_create_account')}") if len(filtered_domains) > 1 and ("domain" in create_response.text.lower() or "address" in create_response.text.lower()):
print(f"{Fore.YELLOW}⚠️ 尝试使用下一个可用域名...{Style.RESET_ALL}")
# 将当前域名添加到屏蔽列表
if selected_domain not in self.blocked_domains:
self.blocked_domains.append(selected_domain)
# 递归调用自己
return self.create_email()
raise Exception(f"{self.translator.get('email.failed_to_create_account') if self.translator else '创建账户失败'}")
except Exception as e:
print(f"{Fore.RED}❌ 创建账户时出错: {str(e)}{Style.RESET_ALL}")
raise
# 获取访问令牌 # 获取访问令牌
token_data = { try:
"address": email, token_data = {
"password": password "address": email,
} "password": password
}
token_response = requests.post(f"{self.api_url}/token", json=token_data)
if token_response.status_code != 200: token_response = requests.post(f"{self.api_url}/token", json=token_data, timeout=10)
raise Exception(f"{self.translator.get('email.failed_to_get_access_token')}") if token_response.status_code != 200:
print(f"{Fore.RED}❌ 获取令牌失败: 状态码 {token_response.status_code}{Style.RESET_ALL}")
print(f"{Fore.RED}❌ 响应内容: {token_response.text}{Style.RESET_ALL}")
raise Exception(f"{self.translator.get('email.failed_to_get_access_token') if self.translator else '获取访问令牌失败'}")
self.token = token_response.json()["token"]
self.email = email
except Exception as e:
print(f"{Fore.RED}❌ 获取令牌时出错: {str(e)}{Style.RESET_ALL}")
raise
self.token = token_response.json()["token"]
self.email = email
if self.translator: if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}") print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
else: else:

View File

@@ -1,4 +1,4 @@
# 設置顏色主題 # set color theme
$Theme = @{ $Theme = @{
Primary = 'Cyan' Primary = 'Cyan'
Success = 'Green' Success = 'Green'
@@ -17,7 +17,7 @@ $Logo = @"
"@ "@
# 美化輸出函數 # Beautiful Output Function
function Write-Styled { function Write-Styled {
param ( param (
[string]$Message, [string]$Message,
@@ -40,7 +40,7 @@ function Write-Styled {
} }
} }
# 獲取版本號函數 # Get version number function
function Get-LatestVersion { function Get-LatestVersion {
try { try {
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest" $latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest"
@@ -54,28 +54,28 @@ function Get-LatestVersion {
} }
} }
# 顯示 Logo # Show Logo
Write-Host $Logo -ForegroundColor $Theme.Primary Write-Host $Logo -ForegroundColor $Theme.Primary
$releaseInfo = Get-LatestVersion $releaseInfo = Get-LatestVersion
$version = $releaseInfo.Version $version = $releaseInfo.Version
Write-Host "Version $version" -ForegroundColor $Theme.Info Write-Host "Version $version" -ForegroundColor $Theme.Info
Write-Host "Created by YeongPin`n" -ForegroundColor $Theme.Info Write-Host "Created by YeongPin`n" -ForegroundColor $Theme.Info
# 設置 TLS 1.2 # Set TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# 主安裝函數 # Main installation function
function Install-CursorFreeVIP { function Install-CursorFreeVIP {
Write-Styled "Start downloading Cursor Free VIP" -Color $Theme.Primary -Prefix "Download" Write-Styled "Start downloading Cursor Free VIP" -Color $Theme.Primary -Prefix "Download"
try { try {
# 獲取最新版本 # Get latest version
Write-Styled "Checking latest version..." -Color $Theme.Primary -Prefix "Update" Write-Styled "Checking latest version..." -Color $Theme.Primary -Prefix "Update"
$releaseInfo = Get-LatestVersion $releaseInfo = Get-LatestVersion
$version = $releaseInfo.Version $version = $releaseInfo.Version
Write-Styled "Found latest version: $version" -Color $Theme.Success -Prefix "Version" Write-Styled "Found latest version: $version" -Color $Theme.Success -Prefix "Version"
# 查找對應的資源 # Find corresponding resources
$asset = $releaseInfo.Assets | Where-Object { $_.name -eq "CursorFreeVIP_${version}_windows.exe" } $asset = $releaseInfo.Assets | Where-Object { $_.name -eq "CursorFreeVIP_${version}_windows.exe" }
if (!$asset) { if (!$asset) {
Write-Styled "File not found: CursorFreeVIP_${version}_windows.exe" -Color $Theme.Error -Prefix "Error" Write-Styled "File not found: CursorFreeVIP_${version}_windows.exe" -Color $Theme.Error -Prefix "Error"
@@ -86,22 +86,114 @@ function Install-CursorFreeVIP {
throw "Cannot find target file" throw "Cannot find target file"
} }
# 下載到Downloads文件夾 # Check if Downloads folder already exists for the corresponding version
$DownloadsPath = [Environment]::GetFolderPath("UserProfile") + "\Downloads" $DownloadsPath = [Environment]::GetFolderPath("UserProfile") + "\Downloads"
$downloadPath = Join-Path $DownloadsPath "CursorFreeVIP.exe" $downloadPath = Join-Path $DownloadsPath "CursorFreeVIP_${version}_windows.exe"
Write-Styled "Downloading to Downloads folder..." -Color $Theme.Primary -Prefix "Download" if (Test-Path $downloadPath) {
Write-Styled "Found existing installation file" -Color $Theme.Success -Prefix "Found"
Write-Styled "Location: $downloadPath" -Color $Theme.Info -Prefix "Location"
# Check if running with administrator privileges
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Styled "Requesting administrator privileges..." -Color $Theme.Warning -Prefix "Admin"
# Create new process with administrator privileges
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = $downloadPath
$startInfo.UseShellExecute = $true
$startInfo.Verb = "runas"
try {
[System.Diagnostics.Process]::Start($startInfo)
Write-Styled "Program started with admin privileges" -Color $Theme.Success -Prefix "Launch"
return
}
catch {
Write-Styled "Failed to start with admin privileges. Starting normally..." -Color $Theme.Warning -Prefix "Warning"
Start-Process $downloadPath
return
}
}
# If already running with administrator privileges, start directly
Start-Process $downloadPath
return
}
Write-Styled "No existing installation file found, starting download..." -Color $Theme.Primary -Prefix "Download"
# Create WebClient and add progress event
$webClient = New-Object System.Net.WebClient $webClient = New-Object System.Net.WebClient
$webClient.Headers.Add("User-Agent", "PowerShell Script") $webClient.Headers.Add("User-Agent", "PowerShell Script")
$webClient.DownloadFile($asset.browser_download_url, $downloadPath)
# Define progress variables
$Global:downloadedBytes = 0
$Global:totalBytes = 0
$Global:lastProgress = 0
$Global:lastBytes = 0
$Global:lastTime = Get-Date
# Download progress event
$eventId = [guid]::NewGuid()
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
$Global:downloadedBytes = $EventArgs.BytesReceived
$Global:totalBytes = $EventArgs.TotalBytesToReceive
$progress = [math]::Round(($Global:downloadedBytes / $Global:totalBytes) * 100, 1)
# Only update display when progress changes by more than 1%
if ($progress -gt $Global:lastProgress + 1) {
$Global:lastProgress = $progress
$downloadedMB = [math]::Round($Global:downloadedBytes / 1MB, 2)
$totalMB = [math]::Round($Global:totalBytes / 1MB, 2)
# Calculate download speed
$currentTime = Get-Date
$timeSpan = ($currentTime - $Global:lastTime).TotalSeconds
if ($timeSpan -gt 0) {
$bytesChange = $Global:downloadedBytes - $Global:lastBytes
$speed = $bytesChange / $timeSpan
# Choose appropriate unit based on speed
$speedDisplay = if ($speed -gt 1MB) {
"$([math]::Round($speed / 1MB, 2)) MB/s"
} elseif ($speed -gt 1KB) {
"$([math]::Round($speed / 1KB, 2)) KB/s"
} else {
"$([math]::Round($speed, 2)) B/s"
}
Write-Host "`rDownloading: $downloadedMB MB / $totalMB MB ($progress%) - $speedDisplay" -NoNewline -ForegroundColor Cyan
# Update last data
$Global:lastBytes = $Global:downloadedBytes
$Global:lastTime = $currentTime
}
}
} | Out-Null
# Download completed event
Register-ObjectEvent -InputObject $webClient -EventName DownloadFileCompleted -Action {
Write-Host "`r" -NoNewline
Write-Styled "Download completed!" -Color $Theme.Success -Prefix "Complete"
Unregister-Event -SourceIdentifier $eventId
} | Out-Null
# Start download
$webClient.DownloadFileAsync([Uri]$asset.browser_download_url, $downloadPath)
# Wait for download to complete
while ($webClient.IsBusy) {
Start-Sleep -Milliseconds 100
}
Write-Styled "Download completed!" -Color $Theme.Success -Prefix "Complete"
Write-Styled "File location: $downloadPath" -Color $Theme.Info -Prefix "Location" Write-Styled "File location: $downloadPath" -Color $Theme.Info -Prefix "Location"
Write-Styled "Starting program..." -Color $Theme.Primary -Prefix "Launch" Write-Styled "Starting program..." -Color $Theme.Primary -Prefix "Launch"
# 運行程序 # Run program
Start-Process $downloadPath Start-Process $downloadPath
} }
catch { catch {
Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "Error" Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "Error"
@@ -109,7 +201,7 @@ function Install-CursorFreeVIP {
} }
} }
# 執行安裝 # Execute installation
try { try {
Install-CursorFreeVIP Install-CursorFreeVIP
} }
@@ -120,4 +212,4 @@ catch {
finally { finally {
Write-Host "`nPress any key to exit..." -ForegroundColor $Theme.Info Write-Host "`nPress any key to exit..." -ForegroundColor $Theme.Info
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
} }

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# 顏色定義 # Color definitions
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
@@ -22,7 +22,7 @@ EOF
echo -e "${NC}" echo -e "${NC}"
} }
# 获取下载文件夹路径 # Get download folder path
get_downloads_dir() { get_downloads_dir() {
if [[ "$(uname)" == "Darwin" ]]; then if [[ "$(uname)" == "Darwin" ]]; then
echo "$HOME/Downloads" echo "$HOME/Downloads"
@@ -36,59 +36,139 @@ get_downloads_dir() {
fi fi
} }
# 獲取最新版本 # Get latest version
get_latest_version() { get_latest_version() {
echo -e "${CYAN} 正在檢查最新版本...${NC}" echo -e "${CYAN} Checking latest version...${NC}"
local latest_release local latest_release
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest) latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo -e "${RED}無法獲取最新版本信息${NC}" echo -e "${RED}Cannot get latest version information${NC}"
exit 1 exit 1
fi fi
VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v') VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v')
echo -e "${GREEN}找到最新版本: ${VERSION}${NC}" echo -e "${GREEN}Found latest version: ${VERSION}${NC}"
} }
# 檢測系統類型 # Detect system type and architecture
detect_os() { detect_os() {
if [[ "$(uname)" == "Darwin" ]]; then if [[ "$(uname)" == "Darwin" ]]; then
OS="mac" # Detect macOS architecture
else ARCH=$(uname -m)
if [[ "$ARCH" == "arm64" ]]; then
OS="mac_arm64"
echo -e "${CYAN} Detected macOS ARM64 architecture${NC}"
else
OS="mac_intel"
echo -e "${CYAN} Detected macOS Intel architecture${NC}"
fi
elif [[ "$(uname)" == "Linux" ]]; then
OS="linux" OS="linux"
echo -e "${CYAN} Detected Linux system${NC}"
else
# Assume Windows
OS="windows"
echo -e "${CYAN} Detected Windows system${NC}"
fi fi
} }
# 下載並安裝 # Install and download
install_cursor_free_vip() { install_cursor_free_vip() {
local downloads_dir=$(get_downloads_dir) local downloads_dir=$(get_downloads_dir)
local binary_name="CursorFreeVIP_${VERSION}_${OS}" local binary_name="CursorFreeVIP_${VERSION}_${OS}"
local binary_path="${downloads_dir}/cursor-free-vip" local binary_path="${downloads_dir}/${binary_name}"
local download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}" local download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
echo -e "${CYAN} 正在下載到 ${downloads_dir}...${NC}" # Check if file already exists
if [ -f "${binary_path}" ]; then
echo -e "${GREEN}✅ Found existing installation file${NC}"
echo -e "${CYAN} Location: ${binary_path}${NC}"
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo -e "${YELLOW}⚠️ Requesting administrator privileges...${NC}"
if command -v sudo >/dev/null 2>&1; then
echo -e "${CYAN} Starting program with sudo...${NC}"
sudo chmod +x "${binary_path}"
sudo "${binary_path}"
else
echo -e "${YELLOW}⚠️ sudo not found, trying to run normally...${NC}"
chmod +x "${binary_path}"
"${binary_path}"
fi
else
# Already running as root
echo -e "${CYAN} Already running as root, starting program...${NC}"
chmod +x "${binary_path}"
"${binary_path}"
fi
return
fi
echo -e "${CYAN} No existing installation file found, starting download...${NC}"
echo -e "${CYAN} Downloading to ${downloads_dir}...${NC}"
echo -e "${CYAN} Download link: ${download_url}${NC}"
# Check if file exists
if curl --output /dev/null --silent --head --fail "$download_url"; then
echo -e "${GREEN}✅ File exists, starting download...${NC}"
else
echo -e "${RED}❌ Download link does not exist: ${download_url}${NC}"
echo -e "${YELLOW}⚠️ Trying without architecture...${NC}"
# Try without architecture
if [[ "$OS" == "mac_arm64" || "$OS" == "mac_intel" ]]; then
OS="mac"
binary_name="CursorFreeVIP_${VERSION}_${OS}"
download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
echo -e "${CYAN} New download link: ${download_url}${NC}"
if ! curl --output /dev/null --silent --head --fail "$download_url"; then
echo -e "${RED}❌ New download link does not exist${NC}"
exit 1
fi
else
exit 1
fi
fi
# Download file
if ! curl -L -o "${binary_path}" "$download_url"; then if ! curl -L -o "${binary_path}" "$download_url"; then
echo -e "${RED}下載失敗${NC}" echo -e "${RED}Download failed${NC}"
exit 1 exit 1
fi fi
echo -e "${CYAN} 正在設置執行權限...${NC}" # Check downloaded file size
local file_size=$(stat -f%z "${binary_path}" 2>/dev/null || stat -c%s "${binary_path}" 2>/dev/null)
echo -e "${CYAN} Downloaded file size: ${file_size} bytes${NC}"
# If file is too small, it might be an error message
if [ "$file_size" -lt 1000 ]; then
echo -e "${YELLOW}⚠️ Warning: Downloaded file is too small, possibly not a valid executable file${NC}"
echo -e "${YELLOW}⚠️ File content:${NC}"
cat "${binary_path}"
echo ""
echo -e "${RED}❌ Download failed, please check version and operating system${NC}"
exit 1
fi
echo -e "${CYAN} Setting executable permissions...${NC}"
chmod +x "${binary_path}" chmod +x "${binary_path}"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo -e "${GREEN}安裝完成!${NC}" echo -e "${GREEN}Installation completed!${NC}"
echo -e "${CYAN} 程序已下載到: ${binary_path}${NC}" echo -e "${CYAN} Program downloaded to: ${binary_path}${NC}"
echo -e "${CYAN} 正在啟動程序...${NC}" echo -e "${CYAN} Starting program...${NC}"
# 直接运行程序 # Run program directly
"${binary_path}" "${binary_path}"
else else
echo -e "${RED}安裝失敗${NC}" echo -e "${RED}Installation failed${NC}"
exit 1 exit 1
fi fi
} }
# 主程序 # Main program
main() { main() {
print_logo print_logo
get_latest_version get_latest_version
@@ -96,5 +176,5 @@ main() {
install_cursor_free_vip install_cursor_free_vip
} }
# 運行主程序 # Run main program
main main