Compare commits

...

9 Commits

9 changed files with 1031129 additions and 91 deletions

97
account_manager.py Normal file
View File

@ -0,0 +1,97 @@
import os
from colorama import Fore, Style
import re
# Define emoji constants
EMOJI = {
'SUCCESS': '',
'ERROR': '',
'INFO': ''
}
class AccountManager:
def __init__(self, translator=None):
self.translator = translator
self.accounts_file = 'cursor_accounts.txt'
def save_account_info(self, email, password, token, total_usage):
"""Save account information to file"""
try:
with open(self.accounts_file, 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {email}\n")
f.write(f"Password: {password}\n")
f.write(f"Token: {token}\n")
f.write(f"Usage Limit: {total_usage}\n")
f.write(f"{'='*50}\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved') if self.translator else 'Account information saved'}...{Style.RESET_ALL}")
return True
except Exception as e:
error_msg = self.translator.get('register.save_account_info_failed', error=str(e)) if self.translator else f'Failed to save account information: {str(e)}'
print(f"{Fore.RED}{EMOJI['ERROR']} {error_msg}{Style.RESET_ALL}")
return False
def get_last_email_domain(self):
"""Get the domain from the last used email"""
try:
if not os.path.exists(self.accounts_file):
return None
# Only read the last 1KB of data from the file
with open(self.accounts_file, 'rb') as f:
# Get file size
f.seek(0, os.SEEK_END)
file_size = f.tell()
if file_size == 0:
return None
# Determine the number of bytes to read, maximum 1KB
read_size = min(1024, file_size)
# Move to the appropriate position to start reading
f.seek(file_size - read_size)
# Read the end data
data = f.read(read_size).decode('utf-8', errors='ignore')
# Split by lines and search in reverse
lines = data.split('\n')
for line in reversed(lines):
if line.strip().startswith('Email:'):
email = line.split('Email:')[1].strip()
# Extract domain part (after @)
if '@' in email:
return email.split('@')[1]
return None
# If no email is found in the last 1KB
return None
except Exception as e:
error_msg = self.translator.get('account.get_last_email_domain_failed', error=str(e)) if self.translator else f'Failed to get the last used email domain: {str(e)}'
print(f"{Fore.RED}{EMOJI['ERROR']} {error_msg}{Style.RESET_ALL}")
return None
def suggest_email(self, first_name, last_name):
"""Generate a suggested email based on first and last name with the last used domain"""
try:
# Get the last used email domain
domain = self.get_last_email_domain()
if not domain:
return None
# Generate email prefix from first and last name (lowercase)
email_prefix = f"{first_name.lower()}.{last_name.lower()}"
# Combine prefix and domain
suggested_email = f"{email_prefix}@{domain}"
return suggested_email
except Exception as e:
error_msg = self.translator.get('account.suggest_email_failed', error=str(e)) if self.translator else f'Failed to suggest email: {str(e)}'
print(f"{Fore.RED}{EMOJI['ERROR']} {error_msg}{Style.RESET_ALL}")
return None

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@ from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter from reset_machine_manual import MachineIDResetter
from get_user_token import get_token_from_cookie from get_user_token import get_token_from_cookie
from config import get_config from config import get_config
from account_manager import AccountManager
os.environ["PYTHONVERBOSE"] = "0" os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0" os.environ["PYINSTALLER_VERBOSE"] = "0"
@ -67,11 +68,28 @@ class CursorRegistration:
def setup_email(self): def setup_email(self):
"""Setup Email""" """Setup Email"""
try: try:
# Try to get a suggested email
account_manager = AccountManager(self.translator)
suggested_email = account_manager.suggest_email(self.first_name, self.last_name)
if suggested_email:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.suggest_email', suggested_email=suggested_email) if self.translator else f'Suggested email: {suggested_email}'}")
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.use_suggested_email_or_enter') if self.translator else 'Type "yes" to use this email or enter your own email:'}")
user_input = input().strip()
if user_input.lower() == 'yes' or user_input.lower() == 'y':
self.email_address = suggested_email
else:
# User input is their own email address
self.email_address = user_input
else:
# If there's no suggested email
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.manual_email_input') if self.translator else 'Please enter your email address:'}") print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.manual_email_input') if self.translator else 'Please enter your email address:'}")
self.email_address = input().strip() self.email_address = input().strip()
# Validate if the email is valid
if '@' not in self.email_address: if '@' not in self.email_address:
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 'Invalid email address'}{Style.RESET_ALL}")
return False return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}" + "\n" + f"{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}" + "\n" + f"{Style.RESET_ALL}")
@ -88,7 +106,7 @@ class CursorRegistration:
code = input().strip() code = input().strip()
if not code.isdigit() or len(code) != 6: if not code.isdigit() or len(code) != 6:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_code') if self.translator else '无效的验证码'}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_code') if self.translator else 'Invalid verification code'}{Style.RESET_ALL}")
return None return None
return code return code
@ -224,17 +242,12 @@ class CursorRegistration:
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
raise Exception("Failed to reset machine ID") raise Exception("Failed to reset machine ID")
# Save account information to file # Save account information to file using AccountManager
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f: account_manager = AccountManager(self.translator)
f.write(f"\n{'='*50}\n") if account_manager.save_account_info(self.email_address, self.password, token, total_usage):
f.write(f"Email: {self.email_address}\n")
f.write(f"Password: {self.password}\n")
f.write(f"Token: {token}\n")
f.write(f"Usage Limit: {total_usage}\n")
f.write(f"{'='*50}\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}")
return True return True
else:
return False
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}")

View File

@ -1,19 +1,23 @@
import requests import requests
import re import re
import datetime import datetime
import time
from typing import Optional from typing import Optional
from .email_tab_interface import EmailTabInterface from .email_tab_interface import EmailTabInterface
class TempMailPlusTab(EmailTabInterface): class TempMailPlusTab(EmailTabInterface):
"""Implementation of EmailTabInterface for tempmail.plus""" """Implementation of EmailTabInterface for tempmail.plus"""
def __init__(self, email: str, epin: str, translator=None): def __init__(self, email: str, epin: str, translator=None,
polling_interval: int = 2, max_attempts: int = 10):
"""Initialize TempMailPlusTab """Initialize TempMailPlusTab
Args: Args:
email: The email address to check email: The email address to check
epin: The epin token for authentication epin: The epin token for authentication
translator: Optional translator for internationalization translator: Optional translator for internationalization
polling_interval: Time in seconds between polling attempts
max_attempts: Maximum number of polling attempts
""" """
self.email = email self.email = email
self.epin = epin self.epin = epin
@ -35,8 +39,13 @@ class TempMailPlusTab(EmailTabInterface):
'x-requested-with': 'XMLHttpRequest' 'x-requested-with': 'XMLHttpRequest'
} }
self.cookies = {'email': email} self.cookies = {'email': email}
self._cached_mail_id = None # 缓存mail_id self._cached_mail_id = None # Cache for mail_id
self._cached_verification_code = None # 缓存验证码 self._cached_verification_code = None # Cache for verification code
# Polling configuration
self.polling_interval = polling_interval
self.max_attempts = max_attempts
self.current_attempt = 0
def refresh_inbox(self) -> None: def refresh_inbox(self) -> None:
"""Refresh the email inbox""" """Refresh the email inbox"""
@ -45,6 +54,42 @@ class TempMailPlusTab(EmailTabInterface):
def check_for_cursor_email(self) -> bool: def check_for_cursor_email(self) -> bool:
"""Check if there is a new email and immediately retrieve verification code """Check if there is a new email and immediately retrieve verification code
Returns:
bool: True if new email found and verification code retrieved, False otherwise
"""
# Reset attempt counter
self.current_attempt = 0
# Polling logic
while self.current_attempt < self.max_attempts:
found = self._check_email_once()
if found:
# Successfully found email and retrieved verification code
self.current_attempt = 0 # Reset counter for next use
return True
# Not found, continue polling
self.current_attempt += 1
if self.current_attempt < self.max_attempts:
# Print polling status information
if self.translator:
print(self.translator.get('tempmail.polling',
attempt=self.current_attempt,
max=self.max_attempts))
else:
print(f"Polling for email: attempt {self.current_attempt}/{self.max_attempts}")
time.sleep(self.polling_interval)
# Exceeded maximum attempts
if self.translator:
print(self.translator.get('tempmail.max_attempts_reached'))
else:
print(f"Max attempts ({self.max_attempts}) reached. No verification email found.")
return False
def _check_email_once(self) -> bool:
"""Single attempt to check for email
Returns: Returns:
bool: True if new email found and verification code retrieved, False otherwise bool: True if new email found and verification code retrieved, False otherwise
""" """
@ -63,11 +108,11 @@ class TempMailPlusTab(EmailTabInterface):
data = response.json() data = response.json()
if data.get('result') and data.get('mail_list'): if data.get('result') and data.get('mail_list'):
# 检查邮件列表中的第一个邮件是否为新邮件 # Check if the first email in the list is a new email
if data['mail_list'][0].get('is_new') == True: if data['mail_list'][0].get('is_new') == True:
self._cached_mail_id = data['mail_list'][0].get('mail_id') # 缓存mail_id self._cached_mail_id = data['mail_list'][0].get('mail_id') # Cache the mail_id
# 立即获取验证码 # Immediately retrieve verification code
verification_code = self._extract_verification_code() verification_code = self._extract_verification_code()
if verification_code: if verification_code:
self._cached_verification_code = verification_code self._cached_verification_code = verification_code
@ -103,7 +148,7 @@ class TempMailPlusTab(EmailTabInterface):
if not data.get('result'): if not data.get('result'):
return "" return ""
# 验证发件人邮箱是否包含cursor字符串 # Verify if sender email contains cursor string
from_mail = data.get('from_mail', '') from_mail = data.get('from_mail', '')
if 'cursor' not in from_mail.lower(): if 'cursor' not in from_mail.lower():
return "" return ""
@ -129,13 +174,12 @@ class TempMailPlusTab(EmailTabInterface):
if __name__ == "__main__": if __name__ == "__main__":
import os import os
import time
import sys import sys
import configparser import configparser
from config import get_config from config import get_config
# 尝试导入 translator # Try to import translator
try: try:
from main import Translator from main import Translator
translator = Translator() translator = Translator()
@ -150,15 +194,15 @@ if __name__ == "__main__":
print(f"{translator.get('tempmail.configured_email', email=email) if translator else f'Configured email: {email}'}") print(f"{translator.get('tempmail.configured_email', email=email) if translator else f'Configured email: {email}'}")
# 初始化TempMailPlusTab传递 translator # Initialize TempMailPlusTab, pass translator
mail_tab = TempMailPlusTab(email, epin, translator) mail_tab = TempMailPlusTab(email, epin, translator)
# 检查是否有Cursor的邮件 # Check if there is a Cursor email
print(f"{translator.get('tempmail.checking_email') if translator else 'Checking for Cursor verification email...'}") print(f"{translator.get('tempmail.checking_email') if translator else 'Checking for Cursor verification email...'}")
if mail_tab.check_for_cursor_email(): if mail_tab.check_for_cursor_email():
print(f"{translator.get('tempmail.email_found') if translator else 'Found Cursor verification email'}") print(f"{translator.get('tempmail.email_found') if translator else 'Found Cursor verification email'}")
# 获取验证码 # Get verification code
verification_code = mail_tab.get_verification_code() verification_code = mail_tab.get_verification_code()
if verification_code: if verification_code:
print(f"{translator.get('tempmail.verification_code', code=verification_code) if translator else f'Verification code: {verification_code}'}") print(f"{translator.get('tempmail.verification_code', code=verification_code) if translator else f'Verification code: {verification_code}'}")

View File

@ -191,6 +191,8 @@
"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",
"suggest_email": "Suggested email: {suggested_email}",
"use_suggested_email_or_enter": "Type \"yes\" to use this email or enter your own email:",
"password": "Password", "password": "Password",
"first_name": "First Name", "first_name": "First Name",
"last_name": "Last Name", "last_name": "Last Name",

View File

@ -191,6 +191,8 @@
"setting_password": "设置密码", "setting_password": "设置密码",
"manual_code_input": "手动输入验证码", "manual_code_input": "手动输入验证码",
"manual_email_input": "手动输入邮箱", "manual_email_input": "手动输入邮箱",
"suggest_email": "推荐邮箱地址: {suggested_email}",
"use_suggested_email_or_enter": "输入\"yes\"使用此邮箱或直接输入您想使用的邮箱地址:",
"password": "密码", "password": "密码",
"first_name": "名字", "first_name": "名字",
"last_name": "姓氏", "last_name": "姓氏",

View File

@ -188,6 +188,8 @@
"setting_password": "設置密碼", "setting_password": "設置密碼",
"manual_code_input": "手動輸入驗證碼", "manual_code_input": "手動輸入驗證碼",
"manual_email_input": "手動輸入郵箱地址", "manual_email_input": "手動輸入郵箱地址",
"suggest_email": "推薦郵箱地址: {suggested_email}",
"use_suggested_email_or_enter": "輸入\"yes\"使用此郵箱或直接輸入您想使用的郵箱地址:",
"password": "密碼", "password": "密碼",
"first_name": "名字", "first_name": "名字",
"last_name": "姓氏", "last_name": "姓氏",

View File

@ -125,75 +125,61 @@ function Install-CursorFreeVIP {
Write-Styled "No existing installation file found, starting download..." -Color $Theme.Primary -Prefix "Download" Write-Styled "No existing installation file found, starting download..." -Color $Theme.Primary -Prefix "Download"
# Create WebClient and add progress event # Use HttpWebRequest for chunked download with real-time progress bar
$webClient = New-Object System.Net.WebClient $url = $asset.browser_download_url
$webClient.Headers.Add("User-Agent", "PowerShell Script") $outputFile = $downloadPath
Write-Styled "Downloading from: $url" -Color $Theme.Info -Prefix "URL"
Write-Styled "Saving to: $outputFile" -Color $Theme.Info -Prefix "Path"
# Define progress variables $request = [System.Net.HttpWebRequest]::Create($url)
$Global:downloadedBytes = 0 $request.UserAgent = "PowerShell Script"
$Global:totalBytes = 0 $response = $request.GetResponse()
$Global:lastProgress = 0 $totalLength = $response.ContentLength
$Global:lastBytes = 0 $responseStream = $response.GetResponseStream()
$Global:lastTime = Get-Date $fileStream = [System.IO.File]::OpenWrite($outputFile)
$buffer = New-Object byte[] 8192
# Download progress event $bytesRead = 0
$eventId = [guid]::NewGuid() $totalRead = 0
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action { $lastProgress = -1
$Global:downloadedBytes = $EventArgs.BytesReceived $startTime = Get-Date
$Global:totalBytes = $EventArgs.TotalBytesToReceive try {
$progress = [math]::Round(($Global:downloadedBytes / $Global:totalBytes) * 100, 1) do {
$bytesRead = $responseStream.Read($buffer, 0, $buffer.Length)
# Only update display when progress changes by more than 1% if ($bytesRead -gt 0) {
if ($progress -gt $Global:lastProgress + 1) { $fileStream.Write($buffer, 0, $bytesRead)
$Global:lastProgress = $progress $totalRead += $bytesRead
$downloadedMB = [math]::Round($Global:downloadedBytes / 1MB, 2) $progress = [math]::Round(($totalRead / $totalLength) * 100, 1)
$totalMB = [math]::Round($Global:totalBytes / 1MB, 2) if ($progress -ne $lastProgress) {
$elapsed = (Get-Date) - $startTime
# Calculate download speed $speed = if ($elapsed.TotalSeconds -gt 0) { $totalRead / $elapsed.TotalSeconds } else { 0 }
$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) { $speedDisplay = if ($speed -gt 1MB) {
"$([math]::Round($speed / 1MB, 2)) MB/s" "{0:N2} MB/s" -f ($speed / 1MB)
} elseif ($speed -gt 1KB) { } elseif ($speed -gt 1KB) {
"$([math]::Round($speed / 1KB, 2)) KB/s" "{0:N2} KB/s" -f ($speed / 1KB)
} else { } else {
"$([math]::Round($speed, 2)) B/s" "{0:N2} B/s" -f $speed
} }
$downloadedMB = [math]::Round($totalRead / 1MB, 2)
Write-Host "`rDownloading: $downloadedMB MB / $totalMB MB ($progress%) - $speedDisplay" -NoNewline -ForegroundColor Cyan $totalMB = [math]::Round($totalLength / 1MB, 2)
Write-Progress -Activity "Downloading CursorFreeVIP" -Status "$downloadedMB MB / $totalMB MB ($progress%) - $speedDisplay" -PercentComplete $progress
# Update last data $lastProgress = $progress
$Global:lastBytes = $Global:downloadedBytes
$Global:lastTime = $currentTime
} }
} }
} | Out-Null } while ($bytesRead -gt 0)
} finally {
# Download completed event $fileStream.Close()
Register-ObjectEvent -InputObject $webClient -EventName DownloadFileCompleted -Action { $responseStream.Close()
Write-Host "`r" -NoNewline $response.Close()
}
Write-Progress -Activity "Downloading CursorFreeVIP" -Completed
# Check file exists and is not zero size
if (!(Test-Path $outputFile) -or ((Get-Item $outputFile).Length -eq 0)) {
throw "Download failed or file is empty."
}
Write-Styled "Download completed!" -Color $Theme.Success -Prefix "Complete" Write-Styled "Download completed!" -Color $Theme.Success -Prefix "Complete"
Unregister-Event -SourceIdentifier $eventId Write-Styled "File location: $outputFile" -Color $Theme.Info -Prefix "Location"
} | 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 "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"
Start-Process $outputFile
# Run program
Start-Process $downloadPath
} }
catch { catch {
Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "Error" Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "Error"