first commit

This commit is contained in:
huangzhenpc
2025-03-11 15:52:54 +08:00
commit 89c5fd4cf9
34 changed files with 40149 additions and 0 deletions

29
.gitignore vendored Normal file
View File

@@ -0,0 +1,29 @@
# PyInstaller
build/
dist/
*.spec
!CursorKeepAlive.mac.spec
!CursorKeepAlive.win.spec
# Python
__pycache__/
*.py[cod]
*$py.class
# Logs
*.log
# IDE
.vscode/
.idea/
# Mac
.DS_Store
venv/
node_modules/
.env
screenshots/

47
README.EN.md Normal file
View File

@@ -0,0 +1,47 @@
# Cursor Pro Automation Tool User Guide
README also available in: [中文](./README.md)
## Online Documentation
[cursor-auto-free-doc.vercel.app](https://cursor-auto-free-doc.vercel.app)
## Note
Recently, some users have sold this software on platforms like Xianyu. Please avoid such practices—there's no need to earn money this way.
## Sponsor for More Updates
![image](./screen/afdian-[未认证]阿臻.jpg)
## License
This project is licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/).
This means you may:
- **Share** — Copy and redistribute the material in any medium or format.
But you must comply with the following conditions:
- **Non-commercial** — You may not use the material for commercial purposes.
## Features
Automated account registration and token refreshing to free your hands.
## Important Notes
1. **Ensure you have Chrome installed. If not, [download here](https://www.google.com/intl/en_pk/chrome/).**
2. **You must log into your account, regardless of its validity. Logged-in is mandatory.**
3. **A stable internet connection is required, preferably via an overseas node. Do not enable global proxy.**
## Configuration Instructions
Please refer to our [online documentation](https://cursor-auto-free-doc.vercel.app) for detailed configuration instructions.
## Download
[https://github.com/chengazhen/cursor-auto-free/releases](https://github.com/chengazhen/cursor-auto-free/releases)
## Update Log
- **2025-01-09**: Added logs and auto-build feature.
- **2025-01-10**: Switched to Cloudflare domain email.
- **2025-01-11**: Added headless mode and proxy configuration through .env file.
- **2025-01-20**: Added IMAP to replace tempmail.plus.
## Special Thanks
This project has received support and help from many open source projects and community members. We would like to express our special gratitude to:
### Open Source Projects
- [go-cursor-help](https://github.com/yuaotian/go-cursor-help) - An excellent Cursor machine code reset tool with 9.1k Stars. Our machine code reset functionality is implemented using this project, which is one of the most popular Cursor auxiliary tools.
Inspired by [gpt-cursor-auto](https://github.com/hmhm2022/gpt-cursor-auto); optimized verification and email auto-registration logic; solved the issue of not being able to receive email verification codes.

BIN
README.md Normal file

Binary file not shown.

131
account_sync.py Normal file
View File

@@ -0,0 +1,131 @@
import os
import json
import time
import random
import string
import requests
import logging
from datetime import datetime
from typing import Optional
from urllib3.exceptions import InsecureRequestWarning
# 禁用 SSL 警告
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
class AccountSync:
def __init__(self, api_choice: int = 1, max_retries: int = 3, retry_delay: int = 5):
self.api_base_url = "https://cursorapi.nosqli.com/admin" # API地址写死
self.api_choice = api_choice # 1=听泉助手池, 2=高质量号池
self.max_retries = max_retries
self.retry_delay = retry_delay
def get_machine_id(self) -> str:
"""生成随机machine_id"""
return "".join(random.choices(string.ascii_letters + string.digits, k=32))
def sync_account(self, account_info: dict) -> bool:
"""同步账号信息到服务器"""
retry_count = 0
while retry_count < self.max_retries:
try:
# 根据选择确定API端点
endpoint = f"{self.api_base_url}/api.account/{'addpool' if self.api_choice == 2 else 'add'}"
logging.info(f"使用API端点: {endpoint}")
headers = {
"Content-Type": "application/json",
"Accept": "application/json"
}
# 获取machine_id
machine_id = self.get_machine_id()
logging.info(f"使用随机生成的 machine_id: {machine_id}")
proxy_host = account_info.get("proxy_host", "")
proxy_port = account_info.get("proxy_port", "")
proxy_username = account_info.get("proxy_username", "")
proxy_password = account_info.get("proxy_password", "")
current_proxies = {
'http': f'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}',
'https': f'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'
}
# 准备请求数据
data = {
"email": account_info["email"],
"password": account_info["password"],
"access_token": account_info.get("token", ""), # 使用token作为access_token
"refresh_token": account_info.get("token", ""), # 使用token作为refresh_token
"machine_id": machine_id,
"user_agent": os.getenv("BROWSER_USER_AGENT", account_info.get("user_agent", ""),),
"registration_time": account_info.get("register_time", datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
"first_name": account_info.get("first_name", ""),
"last_name": account_info.get("last_name", ""),
"ip_address": proxy_host,
"proxy_info": current_proxies,
"proxy_data": account_info.get("proxy_data", ""),
"country": account_info.get("country", ""),
"batch_index": account_info.get("batch_index", "")
}
logging.info(f"请求数据: {json.dumps(data, ensure_ascii=False)}")
# 打印请求信息
logging.info(f"请求 URL: {endpoint}")
logging.info(f"请求数据: {json.dumps(data, ensure_ascii=False)}")
# 发送POST请求
response = requests.post(
endpoint,
json=data,
headers=headers,
timeout=10,
verify=False,
proxies=current_proxies
)
# 打印响应状态和内容
logging.info(f"响应状态码: {response.status_code}")
logging.info(f"响应头: {dict(response.headers)}")
logging.info(f"响应内容: {response.text[:200]}")
if response.status_code == 502:
logging.error("服务器网关错误(502),请检查 API 地址是否正确")
time.sleep(self.retry_delay)
retry_count += 1
continue
try:
response_data = response.json()
if response_data["code"] == 200 and response_data.get("msg") == "添加成功":
logging.info(f"账号 {account_info['email']} 同步成功")
return True
elif response_data["code"] == 400:
logging.warning(f"账号 {account_info['email']} 已存在")
return False
elif response_data["code"] == 500:
logging.error(f"服务器内部错误(500),第 {retry_count + 1} 次重试")
time.sleep(self.retry_delay)
retry_count += 1
continue
else:
logging.error(f"账号 {account_info['email']} 同步失败: {response_data.get('msg', '未知错误')}")
time.sleep(self.retry_delay)
retry_count += 1
continue
except json.JSONDecodeError:
logging.error(f"服务器返回的不是有效的JSON数据: {response.text[:200]}")
time.sleep(self.retry_delay)
retry_count += 1
continue
except requests.exceptions.RequestException as e:
logging.error(f"请求失败: {str(e)}")
time.sleep(self.retry_delay)
retry_count += 1
continue
except Exception as e:
logging.error(f"同步账号 {account_info['email']} 时出错: {str(e)}")
time.sleep(self.retry_delay)
retry_count += 1
continue
logging.error(f"账号 {account_info['email']} 同步失败,已重试 {self.max_retries}")
return False

54
backup/.env.example Normal file
View File

@@ -0,0 +1,54 @@
# 临时邮箱配置
DOMAIN=nosqli.com
TEMP_MAIL=awegko
TEMP_MAIL_EXT=@mailto.plus
BROWSER_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36
MAIL_SERVER=https://tempmail.plus
DOMAIN=586vip.cn
TEMP_MAIL=ademyyk
TEMP_MAIL_EXT=@mailto.plus
BROWSER_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36
MAIL_SERVER=https://tempmail.plus
DOMAIN=wuen.site
TEMP_MAIL=actoke
TEMP_MAIL_EXT=@mailto.plus
BROWSER_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36
MAIL_SERVER=https://tempmail.plus
DOMAIN=jxyweb.site
TEMP_MAIL=exvet
TEMP_MAIL_EXT=@mailto.plus
BROWSER_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36
MAIL_SERVER=https://tempmail.plus
TEMP_MAIL_EPIN=889944
[
{
"DOMAIN": "nosqli.com",
"TEMP_MAIL": "ozamdyw",
"TEMP_MAIL_EXT": "@mailto.plus",
"BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
"MAIL_SERVER": "https://tempmail.plus"
},
{
"DOMAIN": "nosqli.com",
"TEMP_MAIL": "xkwmvp",
"TEMP_MAIL_EXT": "@mailto.plus",
"BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
"MAIL_SERVER": "https://tempmail.plus"
},
{
"DOMAIN": "nosqli.com",
"TEMP_MAIL": "yzqnlr",
"TEMP_MAIL_EXT": "@mailto.plus",
"BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
"MAIL_SERVER": "https://tempmail.plus"
}
]

212
backup/config.py Normal file
View File

@@ -0,0 +1,212 @@
from dotenv import load_dotenv
import os
import sys
import json
import random
from logger import logging
# 预定义的配置列表
CONFIGS = [
# {
# "DOMAIN": "nosqli.com",
# "TEMP_MAIL": "awegko",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": ""
# },
# {
# "DOMAIN": "586vip.cn",
# "TEMP_MAIL": "emufu",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": ""
# },
# {
# "DOMAIN": "wuen.site",
# "TEMP_MAIL": "actoke",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": ""
# },
# {
# "DOMAIN": "jxyweb.site",
# "TEMP_MAIL": "exvet",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# # "TEMP_MAIL_EPIN": "889944"
# },
# {
# "DOMAIN": "cursorpro.asia",
# "TEMP_MAIL": "ybeudu",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": ""
# },
# {
# "DOMAIN": "cursorpro.xyz",
# "TEMP_MAIL": "neyxwub",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": ""
# },
# {
# "DOMAIN": "cursorpro.xin",
# "TEMP_MAIL": "qeznosa",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": ""
# },
{
"DOMAIN": "cursorpro.com.cn",
"TEMP_MAIL": "oducbum",
"TEMP_MAIL_EXT": "@mailto.plus",
"BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
"MAIL_SERVER": "https://tempmail.plus",
"TEMP_MAIL_EPIN": ""
},
# {
# "DOMAIN": "jpgoogle.online",
# "TEMP_MAIL": "oahpuza",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": ""
# },
# {
# "DOMAIN": "jpgoogle.asia",
# "TEMP_MAIL": "emufu",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": ""
# },
{
"DOMAIN": "jpgoogle.xyz",
"TEMP_MAIL": "ybeudu",
"TEMP_MAIL_EXT": "@mailto.plus",
"BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
"MAIL_SERVER": "https://tempmail.plus",
"TEMP_MAIL_EPIN": ""
},
{
"DOMAIN": "jpgoogle.xin",
"TEMP_MAIL": "actoke",
"TEMP_MAIL_EXT": "@mailto.plus",
"BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
"MAIL_SERVER": "https://tempmail.plus",
"TEMP_MAIL_EPIN": ""
},
# {
# "DOMAIN": "tengxu2024.xyz",
# "TEMP_MAIL": "exvet",
# "TEMP_MAIL_EXT": "@mailto.plus",
# "BROWSER_USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36",
# "MAIL_SERVER": "https://tempmail.plus",
# "TEMP_MAIL_EPIN": "889944"
# },
]
class Config:
def __init__(self, config_index=None):
# 获取应用程序的根目录路径
if getattr(sys, "frozen", False):
# 如果是打包后的可执行文件
application_path = os.path.dirname(sys.executable)
else:
# 如果是开发环境
application_path = os.path.dirname(os.path.abspath(__file__))
# 如果指定了索引,使用指定配置,否则随机选择
if config_index is not None and 0 <= config_index < len(CONFIGS):
self.current_config = CONFIGS[config_index]
else:
self.current_config = random.choice(CONFIGS)
# 设置当前配置
self.domain = self.current_config.get("DOMAIN", "")
self.temp_mail = self.current_config.get("TEMP_MAIL", "").strip()
self.temp_mail_ext = self.current_config.get("TEMP_MAIL_EXT", "").strip()
self.temp_mail_epin = self.current_config.get("TEMP_MAIL_EPIN", "").strip()
self.browser_user_agent = self.current_config.get("BROWSER_USER_AGENT", "")
self.mail_server = self.current_config.get("MAIL_SERVER", "")
# IMAP相关配置
self.imap = False
if self.temp_mail == "null":
self.imap = True
self.imap_server = self.current_config.get("IMAP_SERVER", "")
self.imap_port = self.current_config.get("IMAP_PORT", "")
self.imap_user = self.current_config.get("IMAP_USER", "")
self.imap_pass = self.current_config.get("IMAP_PASS", "")
self.imap_dir = self.current_config.get("IMAP_DIR", "inbox")
def get_config_count(self):
"""获取配置总数"""
return len(CONFIGS)
def get_temp_mail(self):
return self.temp_mail
def get_temp_mail_ext(self):
return self.temp_mail_ext
def get_temp_mail_epin(self):
return self.temp_mail_epin
def get_domain(self):
return self.domain
def get_browser_user_agent(self):
return self.browser_user_agent
def get_mail_server(self):
return self.mail_server
def get_imap(self):
"""获取IMAP配置"""
if not self.imap:
return False
return {
"imap_server": self.imap_server,
"imap_port": self.imap_port,
"imap_user": self.imap_user,
"imap_pass": self.imap_pass,
"imap_dir": self.imap_dir,
}
def print_config(self):
"""打印当前使用的配置"""
logging.info("\n=== 当前使用的配置 ===")
if self.imap:
logging.info(f"IMAP服务器: {self.imap_server}")
logging.info(f"IMAP端口: {self.imap_port}")
logging.info(f"IMAP用户名: {self.imap_user}")
logging.info(f"IMAP密码: {'*' * len(self.imap_pass)}")
logging.info(f"IMAP收件箱目录: {self.imap_dir}")
else:
logging.info(f"域名: {self.domain}")
logging.info(f"临时邮箱: {self.temp_mail}{self.temp_mail_ext}")
logging.info(f"邮件服务器: {self.mail_server}")
logging.info("=" * 20)
# 使用示例
if __name__ == "__main__":
try:
config = Config()
print("环境变量加载成功!")
config.print_config()
except ValueError as e:
print(f"错误: {e}")

View File

@@ -0,0 +1,86 @@
import sqlite3
import os
import sys
class CursorAuthManager:
"""Cursor认证信息管理器"""
def __init__(self):
# 判断操作系统
if sys.platform == "win32": # Windows
appdata = os.getenv("APPDATA")
if appdata is None:
raise EnvironmentError("APPDATA 环境变量未设置")
self.db_path = os.path.join(
appdata, "Cursor", "User", "globalStorage", "state.vscdb"
)
elif sys.platform == "darwin": # macOS
self.db_path = os.path.abspath(os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
))
elif sys.platform == "linux" : # Linux 和其他类Unix系统
self.db_path = os.path.abspath(os.path.expanduser(
"~/.config/Cursor/User/globalStorage/state.vscdb"
))
else:
raise NotImplementedError(f"不支持的操作系统: {sys.platform}")
def update_auth(self, email=None, access_token=None, refresh_token=None):
"""
更新Cursor的认证信息
:param email: 新的邮箱地址
:param access_token: 新的访问令牌
:param refresh_token: 新的刷新令牌
:return: bool 是否成功更新
"""
updates = []
# 登录状态
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
if email is not None:
updates.append(("cursorAuth/cachedEmail", email))
if access_token is not None:
updates.append(("cursorAuth/accessToken", access_token))
if refresh_token is not None:
updates.append(("cursorAuth/refreshToken", refresh_token))
if not updates:
print("没有提供任何要更新的值")
return False
conn = None
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
for key, value in updates:
# 如果没有更新任何行,说明key不存在,执行插入
# 检查 accessToken 是否存在
check_query = f"SELECT COUNT(*) FROM itemTable WHERE key = ?"
cursor.execute(check_query, (key,))
if cursor.fetchone()[0] == 0:
insert_query = "INSERT INTO itemTable (key, value) VALUES (?, ?)"
cursor.execute(insert_query, (key, value))
else:
update_query = "UPDATE itemTable SET value = ? WHERE key = ?"
cursor.execute(update_query, (value, key))
if cursor.rowcount > 0:
print(f"成功更新 {key.split('/')[-1]}")
else:
print(f"未找到 {key.split('/')[-1]} 或值未变化")
conn.commit()
return True
except sqlite3.Error as e:
print("数据库错误:", str(e))
return False
except Exception as e:
print("发生错误:", str(e))
return False
finally:
if conn:
conn.close()

View File

@@ -0,0 +1,504 @@
import os
import platform
import json
import sys
from colorama import Fore, Style
from enum import Enum
from typing import Optional
from exit_cursor import ExitCursor
import go_cursor_help
import patch_cursor_get_machine_id
from reset_machine import MachineIDResetter
os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0"
import time
import random
from cursor_auth_manager import CursorAuthManager
import os
from logger import logging
from browser_utils import BrowserManager
from get_email_code import EmailVerificationHandler
from logo import print_logo
from config import Config
from datetime import datetime
# 定义 EMOJI 字典
EMOJI = {"ERROR": "", "WARNING": "⚠️", "INFO": ""}
class VerificationStatus(Enum):
"""验证状态枚举"""
PASSWORD_PAGE = "@name=password"
CAPTCHA_PAGE = "@data-index=0"
ACCOUNT_SETTINGS = "Account Settings"
class TurnstileError(Exception):
"""Turnstile 验证相关异常"""
pass
def save_screenshot(tab, stage: str, timestamp: bool = True) -> None:
"""
保存页面截图
Args:
tab: 浏览器标签页对象
stage: 截图阶段标识
timestamp: 是否添加时间戳
"""
try:
# 创建 screenshots 目录
screenshot_dir = "screenshots"
if not os.path.exists(screenshot_dir):
os.makedirs(screenshot_dir)
# 生成文件名
if timestamp:
filename = f"turnstile_{stage}_{int(time.time())}.png"
else:
filename = f"turnstile_{stage}.png"
filepath = os.path.join(screenshot_dir, filename)
# 保存截图
tab.get_screenshot(filepath)
logging.debug(f"截图已保存: {filepath}")
except Exception as e:
logging.warning(f"截图保存失败: {str(e)}")
def check_verification_success(tab) -> Optional[VerificationStatus]:
"""
检查验证是否成功
Returns:
VerificationStatus: 验证成功时返回对应状态,失败返回 None
"""
for status in VerificationStatus:
if tab.ele(status.value):
logging.info(f"验证成功 - 已到达{status.name}页面")
return status
return None
def handle_turnstile(tab, max_retries: int = 2, retry_interval: tuple = (1, 2)) -> bool:
"""
处理 Turnstile 验证
Args:
tab: 浏览器标签页对象
max_retries: 最大重试次数
retry_interval: 重试间隔时间范围(最小值, 最大值)
Returns:
bool: 验证是否成功
Raises:
TurnstileError: 验证过程中出现异常
"""
logging.info("正在检测 Turnstile 验证...")
save_screenshot(tab, "start")
retry_count = 0
try:
while retry_count < max_retries:
retry_count += 1
logging.debug(f"{retry_count} 次尝试验证")
try:
# 定位验证框元素
challenge_check = (
tab.ele("@id=cf-turnstile", timeout=2)
.child()
.shadow_root.ele("tag:iframe")
.ele("tag:body")
.sr("tag:input")
)
if challenge_check:
logging.info("检测到 Turnstile 验证框,开始处理...")
# 随机延时后点击验证
time.sleep(random.uniform(1, 3))
challenge_check.click()
time.sleep(2)
# 保存验证后的截图
save_screenshot(tab, "clicked")
# 检查验证结果
if check_verification_success(tab):
logging.info("Turnstile 验证通过")
save_screenshot(tab, "success")
return True
except Exception as e:
logging.debug(f"当前尝试未成功: {str(e)}")
# 检查是否已经验证成功
if check_verification_success(tab):
return True
# 随机延时后继续下一次尝试
time.sleep(random.uniform(*retry_interval))
# 超出最大重试次数
logging.error(f"验证失败 - 已达到最大重试次数 {max_retries}")
logging.error(
"请前往开源项目查看更多信息https://github.com/chengazhen/cursor-auto-free"
)
save_screenshot(tab, "failed")
return False
except Exception as e:
error_msg = f"Turnstile 验证过程发生异常: {str(e)}"
logging.error(error_msg)
save_screenshot(tab, "error")
raise TurnstileError(error_msg)
def get_cursor_session_token(tab, max_attempts=3, retry_interval=2):
"""
获取Cursor会话token带有重试机制
:param tab: 浏览器标签页
:param max_attempts: 最大尝试次数
:param retry_interval: 重试间隔(秒)
:return: session token 或 None
"""
logging.info("开始获取cookie")
attempts = 0
while attempts < max_attempts:
try:
cookies = tab.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
return cookie["value"].split("%3A%3A")[1]
attempts += 1
if attempts < max_attempts:
logging.warning(
f"{attempts} 次尝试未获取到CursorSessionToken{retry_interval}秒后重试..."
)
time.sleep(retry_interval)
else:
logging.error(
f"已达到最大尝试次数({max_attempts})获取CursorSessionToken失败"
)
except Exception as e:
logging.error(f"获取cookie失败: {str(e)}")
attempts += 1
if attempts < max_attempts:
logging.info(f"将在 {retry_interval} 秒后重试...")
time.sleep(retry_interval)
return None
def update_cursor_auth(email=None, access_token=None, refresh_token=None):
"""
更新Cursor的认证信息的便捷函数
"""
auth_manager = CursorAuthManager()
return auth_manager.update_auth(email, access_token, refresh_token)
def sign_up_account(browser, tab):
logging.info("=== 开始注册账号流程 ===")
logging.info(f"正在访问注册页面: {sign_up_url}")
tab.get(sign_up_url)
try:
if tab.ele("@name=first_name"):
logging.info("正在填写个人信息...")
tab.actions.click("@name=first_name").input(first_name)
logging.info(f"已输入名字: {first_name}")
time.sleep(random.uniform(1, 3))
tab.actions.click("@name=last_name").input(last_name)
logging.info(f"已输入姓氏: {last_name}")
time.sleep(random.uniform(1, 3))
tab.actions.click("@name=email").input(account)
logging.info(f"已输入邮箱: {account}")
time.sleep(random.uniform(1, 3))
logging.info("提交个人信息...")
tab.actions.click("@type=submit")
except Exception as e:
logging.error(f"注册页面访问失败: {str(e)}")
return False
handle_turnstile(tab)
try:
if tab.ele("@name=password"):
logging.info("正在设置密码...")
tab.ele("@name=password").input(password)
time.sleep(random.uniform(1, 3))
logging.info("提交密码...")
tab.ele("@type=submit").click()
logging.info("密码设置完成,等待系统响应...")
except Exception as e:
logging.error(f"密码设置失败: {str(e)}")
return False
if tab.ele("This email is not available."):
logging.error("注册失败:邮箱已被使用")
return False
handle_turnstile(tab)
while True:
try:
if tab.ele("Account Settings"):
logging.info("注册成功 - 已进入账户设置页面")
break
if tab.ele("@data-index=0"):
logging.info("正在获取邮箱验证码...")
code = email_handler.get_verification_code()
if not code:
logging.error("获取验证码失败")
return False
logging.info(f"成功获取验证码: {code}")
logging.info("正在输入验证码...")
i = 0
for digit in code:
tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3))
i += 1
logging.info("验证码输入完成")
break
except Exception as e:
logging.error(f"验证码处理过程出错: {str(e)}")
handle_turnstile(tab)
wait_time = random.randint(3, 6)
for i in range(wait_time):
logging.info(f"等待系统处理中... 剩余 {wait_time-i}")
time.sleep(1)
logging.info("正在获取账户信息...")
tab.get(settings_url)
try:
usage_selector = (
"css:div.col-span-2 > div > div > div > div > "
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
)
usage_ele = tab.ele(usage_selector)
if usage_ele:
usage_info = usage_ele.text
total_usage = usage_info.split("/")[-1].strip()
logging.info(f"账户可用额度上限: {total_usage}")
logging.info(
"请前往开源项目查看更多信息https://github.com/chengazhen/cursor-auto-free"
)
except Exception as e:
logging.error(f"获取账户额度信息失败: {str(e)}")
logging.info("\n=== 注册完成 ===")
account_info = f"Cursor 账号信息:\n邮箱: {account}\n密码: {password}"
logging.info(account_info)
time.sleep(5)
return True
class EmailGenerator:
def __init__(
self,
password="".join(
random.choices(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*",
k=12,
)
),
):
configInstance = Config()
configInstance.print_config()
self.domain = configInstance.get_domain()
self.default_password = password
self.default_first_name = self.generate_random_name()
self.default_last_name = self.generate_random_name()
def generate_random_name(self, length=6):
"""生成随机用户名"""
first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
rest_letters = "".join(
random.choices("abcdefghijklmnopqrstuvwxyz", k=length - 1)
)
return first_letter + rest_letters
def generate_email(self, length=8):
"""生成随机邮箱地址"""
random_str = "".join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length))
timestamp = str(int(time.time()))[-6:] # 使用时间戳后6位
return f"{random_str}{timestamp}@{self.domain}"
def get_account_info(self):
"""获取完整的账号信息"""
return {
"email": self.generate_email(),
"password": self.default_password,
"first_name": self.default_first_name,
"last_name": self.default_last_name,
}
def get_user_agent():
"""获取user_agent"""
try:
# 使用JavaScript获取user agent
browser_manager = BrowserManager()
browser = browser_manager.init_browser()
user_agent = browser.latest_tab.run_js("return navigator.userAgent")
browser_manager.quit()
return user_agent
except Exception as e:
logging.error(f"获取user agent失败: {str(e)}")
return None
def check_cursor_version():
"""检查cursor版本"""
pkg_path, main_path = patch_cursor_get_machine_id.get_cursor_paths()
with open(pkg_path, "r", encoding="utf-8") as f:
version = json.load(f)["version"]
return patch_cursor_get_machine_id.version_check(version, min_version="0.45.0")
def reset_machine_id(greater_than_0_45):
if greater_than_0_45:
# 提示请手动执行脚本 https://github.com/chengazhen/cursor-auto-free/blob/main/patch_cursor_get_machine_id.py
go_cursor_help.go_cursor_help()
else:
MachineIDResetter().reset_machine_ids()
def print_end_message():
logging.info("\n\n\n\n\n")
logging.info("=" * 30)
logging.info("所有操作已完成")
logging.info("\n=== 获取更多信息 ===")
logging.info("🔥 QQ交流群: 1034718338")
logging.info("📺 B站UP主: 想回家的前端")
logging.info("=" * 30)
logging.info(
"请前往开源项目查看更多信息https://github.com/chengazhen/cursor-auto-free"
)
if __name__ == "__main__":
print_logo()
greater_than_0_45 = 8.8
browser_manager = None
try:
logging.info("\n=== 初始化程序 ===")
# ExitCursor()
# 提示用户选择操作模式
print("\n请选择操作模式:")
print("1. 仅重置机器码")
print("2. 完整注册流程")
while True:
try:
choice = int(input("请输入选项 (1 或 2): ").strip())
if choice in [1, 2]:
break
else:
print("无效的选项,请重新输入")
except ValueError:
print("请输入有效的数字")
if choice == 1:
# 仅执行重置机器码
reset_machine_id(greater_than_0_45)
logging.info("机器码重置完成")
print_end_message()
sys.exit(0)
logging.info("正在初始化浏览器...")
# 获取user_agent
user_agent = get_user_agent()
if not user_agent:
logging.error("获取user agent失败使用默认值")
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
# 剔除user_agent中的"HeadlessChrome"
user_agent = user_agent.replace("HeadlessChrome", "Chrome")
browser_manager = BrowserManager()
browser = browser_manager.init_browser(user_agent)
# 获取并打印浏览器的user-agent
user_agent = browser.latest_tab.run_js("return navigator.userAgent")
logging.info("正在初始化邮箱验证模块...")
email_handler = EmailVerificationHandler()
logging.info(
"请前往开源项目查看更多信息https://github.com/chengazhen/cursor-auto-free"
)
logging.info("\n=== 配置信息 ===")
login_url = "https://authenticator.cursor.sh"
sign_up_url = "https://authenticator.cursor.sh/sign-up"
settings_url = "https://www.cursor.com/settings"
mail_url = "https://tempmail.plus"
logging.info("正在生成随机账号信息...")
email_generator = EmailGenerator()
account = email_generator.generate_email()
password = email_generator.default_password
first_name = email_generator.default_first_name
last_name = email_generator.default_last_name
logging.info(f"生成的邮箱账号: {account}")
auto_update_cursor_auth = True
tab = browser.latest_tab
tab.run_js("try { turnstile.reset() } catch(e) { }")
logging.info("\n=== 开始注册流程 ===")
logging.info(f"正在访问登录页面: {login_url}")
tab.get(login_url)
if sign_up_account(browser, tab):
logging.info("正在获取会话令牌...")
token = get_cursor_session_token(tab)
if token:
logging.info("更新认证信息...")
update_cursor_auth(
email=account, access_token=token, refresh_token=token
)
logging.info(
"请前往开源项目查看更多信息https://github.com/chengazhen/cursor-auto-free"
)
logging.info("重置机器码...")
reset_machine_id(greater_than_0_45)
logging.info("所有操作已完成")
print_end_message()
else:
logging.error("获取会话令牌失败,注册流程未完成")
except Exception as e:
logging.error(f"程序执行出现错误: {str(e)}")
import traceback
logging.error(traceback.format_exc())
finally:
if browser_manager:
browser_manager.quit()
input("\n程序执行完毕,按回车键退出...")

68
backup/exit_cursor.py Normal file
View File

@@ -0,0 +1,68 @@
import psutil
from logger import logging
import time
def ExitCursor(timeout=5):
"""
温和地关闭 Cursor 进程
Args:
timeout (int): 等待进程自然终止的超时时间(秒)
Returns:
bool: 是否成功关闭所有进程
"""
try:
logging.info("开始退出Cursor...")
cursor_processes = []
# 收集所有 Cursor 进程
for proc in psutil.process_iter(['pid', 'name']):
try:
if proc.info['name'].lower() in ['cursor.exe', 'cursor']:
cursor_processes.append(proc)
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
if not cursor_processes:
logging.info("未发现运行中的 Cursor 进程")
return True
# 温和地请求进程终止
for proc in cursor_processes:
try:
if proc.is_running():
proc.terminate() # 发送终止信号
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
# 等待进程自然终止
start_time = time.time()
while time.time() - start_time < timeout:
still_running = []
for proc in cursor_processes:
try:
if proc.is_running():
still_running.append(proc)
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
if not still_running:
logging.info("所有 Cursor 进程已正常关闭")
return True
# 等待一小段时间再检查
time.sleep(0.5)
# 如果超时后仍有进程在运行
if still_running:
process_list = ", ".join([str(p.pid) for p in still_running])
logging.warning(f"以下进程未能在规定时间内关闭: {process_list}")
return False
return True
except Exception as e:
logging.error(f"关闭 Cursor 进程时发生错误: {str(e)}")
return False
if __name__ == "__main__":
ExitCursor()

View File

@@ -0,0 +1,126 @@
from DrissionPage import ChromiumOptions, Chromium
from DrissionPage.common import Keys
import time
import re
import sys
import os
def get_extension_path():
"""获取插件路径"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "turnstilePatch")
if hasattr(sys, "_MEIPASS"):
print("运行在打包环境中")
extension_path = os.path.join(sys._MEIPASS, "turnstilePatch")
print(f"尝试加载插件路径: {extension_path}")
if not os.path.exists(extension_path):
raise FileNotFoundError(
f"插件不存在: {extension_path}\n请确保 turnstilePatch 文件夹在正确位置"
)
return extension_path
def get_browser_options():
co = ChromiumOptions()
try:
extension_path = get_extension_path()
co.add_extension(extension_path)
except FileNotFoundError as e:
print(f"警告: {e}")
co.set_user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36"
)
co.set_pref("credentials_enable_service", False)
co.set_argument("--hide-crash-restore-bubble")
co.auto_port()
# Mac 系统特殊处理
if sys.platform == "darwin":
co.set_argument("--no-sandbox")
co.set_argument("--disable-gpu")
return co
def get_veri_code(username):
# 使用相同的浏览器配置
co = get_browser_options()
browser = Chromium(co)
code = None
try:
# 获取当前标签页
tab = browser.latest_tab
tab.run_js("try { turnstile.reset() } catch(e) { }")
# 打开临时邮箱网站
tab.get("https://tempmail.plus/zh")
time.sleep(2)
# 设置邮箱用户名
while True:
if tab.ele("@id=pre_button"):
# 点击输入框
tab.actions.click("@id=pre_button")
time.sleep(1)
# 删除之前的内容
tab.run_js('document.getElementById("pre_button").value = ""')
# 输入新用户名并回车
tab.actions.input(username).key_down(Keys.ENTER).key_up(Keys.ENTER)
break
time.sleep(1)
# 等待并获取新邮件
while True:
new_mail = tab.ele("@class=mail")
if new_mail:
if new_mail.text:
print("最新的邮件:", new_mail.text)
tab.actions.click("@class=mail")
break
else:
print(new_mail)
break
time.sleep(1)
# 提取验证码
if tab.ele("@class=overflow-auto mb-20"):
email_content = tab.ele("@class=overflow-auto mb-20").text
verification_code = re.search(
r"verification code is (\d{6})", email_content
)
if verification_code:
code = verification_code.group(1)
print("验证码:", code)
else:
print("未找到验证码")
# 删除邮件
if tab.ele("@id=delete_mail"):
tab.actions.click("@id=delete_mail")
time.sleep(1)
if tab.ele("@id=confirm_mail"):
tab.actions.click("@id=confirm_mail")
print("删除邮件")
except Exception as e:
print(f"发生错误: {str(e)}")
finally:
browser.quit()
return code
# 测试运行
if __name__ == "__main__":
test_username = "test_user" # 替换为你要测试的用户名
code = get_veri_code(test_username)
print(f"获取到的验证码: {code}")

29
backup/go_cursor_help.py Normal file
View File

@@ -0,0 +1,29 @@
import platform
import os
import subprocess
from logger import logging
def go_cursor_help():
system = platform.system()
logging.info(f"当前操作系统: {system}")
base_url = "https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run"
if system == "Darwin": # macOS
cmd = f'curl -fsSL {base_url}/cursor_mac_id_modifier.sh | sudo bash'
logging.info("执行macOS命令")
os.system(cmd)
elif system == "Linux":
cmd = f'curl -fsSL {base_url}/cursor_linux_id_modifier.sh | sudo bash'
logging.info("执行Linux命令")
os.system(cmd)
elif system == "Windows":
cmd = f'irm {base_url}/cursor_win_id_modifier.ps1 | iex'
logging.info("执行Windows命令")
# 在Windows上使用PowerShell执行命令
subprocess.run(["powershell", "-Command", cmd], shell=True)
else:
logging.error(f"不支持的操作系统: {system}")
return False
return True

View File

@@ -0,0 +1,302 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import logging
import os
import platform
import re
import shutil
import sys
import tempfile
from typing import Tuple
# 配置日志
def setup_logging() -> logging.Logger:
"""配置并返回logger实例"""
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter(
"%(asctime)s - %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
logger = setup_logging()
def get_cursor_paths() -> Tuple[str, str]:
"""
根据不同操作系统获取 Cursor 相关路径
Returns:
Tuple[str, str]: (package.json路径, main.js路径)的元组
Raises:
OSError: 当找不到有效路径或系统不支持时抛出
"""
system = platform.system()
paths_map = {
"Darwin": {
"base": "/Applications/Cursor.app/Contents/Resources/app",
"package": "package.json",
"main": "out/main.js",
},
"Windows": {
"base": os.path.join(
os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"
),
"package": "package.json",
"main": "out/main.js",
},
"Linux": {
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
"package": "package.json",
"main": "out/main.js",
},
}
if system not in paths_map:
raise OSError(f"不支持的操作系统: {system}")
if system == "Linux":
for base in paths_map["Linux"]["bases"]:
pkg_path = os.path.join(base, paths_map["Linux"]["package"])
if os.path.exists(pkg_path):
return (pkg_path, os.path.join(base, paths_map["Linux"]["main"]))
raise OSError("在 Linux 系统上未找到 Cursor 安装路径")
base_path = paths_map[system]["base"]
return (
os.path.join(base_path, paths_map[system]["package"]),
os.path.join(base_path, paths_map[system]["main"]),
)
def check_system_requirements(pkg_path: str, main_path: str) -> bool:
"""
检查系统要求
Args:
pkg_path: package.json 文件路径
main_path: main.js 文件路径
Returns:
bool: 检查是否通过
"""
for file_path in [pkg_path, main_path]:
if not os.path.isfile(file_path):
logger.error(f"文件不存在: {file_path}")
return False
if not os.access(file_path, os.W_OK):
logger.error(f"没有文件写入权限: {file_path}")
return False
return True
def version_check(version: str, min_version: str = "", max_version: str = "") -> bool:
"""
版本号检查
Args:
version: 当前版本号
min_version: 最小版本号要求
max_version: 最大版本号要求
Returns:
bool: 版本号是否符合要求
"""
version_pattern = r"^\d+\.\d+\.\d+$"
try:
if not re.match(version_pattern, version):
logger.error(f"无效的版本号格式: {version}")
return False
def parse_version(ver: str) -> Tuple[int, ...]:
return tuple(map(int, ver.split(".")))
current = parse_version(version)
if min_version and current < parse_version(min_version):
logger.error(f"版本号 {version} 小于最小要求 {min_version}")
return False
if max_version and current > parse_version(max_version):
logger.error(f"版本号 {version} 大于最大要求 {max_version}")
return False
return True
except Exception as e:
logger.error(f"版本检查失败: {str(e)}")
return False
def modify_main_js(main_path: str) -> bool:
"""
修改 main.js 文件
Args:
main_path: main.js 文件路径
Returns:
bool: 修改是否成功
"""
try:
# 获取原始文件的权限和所有者信息
original_stat = os.stat(main_path)
original_mode = original_stat.st_mode
original_uid = original_stat.st_uid
original_gid = original_stat.st_gid
with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file:
with open(main_path, "r", encoding="utf-8") as main_file:
content = main_file.read()
# 执行替换
patterns = {
r"async getMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMachineId(){return \1}",
r"async getMacMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMacMachineId(){return \1}",
}
for pattern, replacement in patterns.items():
content = re.sub(pattern, replacement, content)
tmp_file.write(content)
tmp_path = tmp_file.name
# 使用 shutil.copy2 保留文件权限
shutil.copy2(main_path, main_path + ".old")
shutil.move(tmp_path, main_path)
# 恢复原始文件的权限和所有者
os.chmod(main_path, original_mode)
if os.name != "nt": # 在非Windows系统上设置所有者
os.chown(main_path, original_uid, original_gid)
logger.info("文件修改成功")
return True
except Exception as e:
logger.error(f"修改文件时发生错误: {str(e)}")
if "tmp_path" in locals():
os.unlink(tmp_path)
return False
def backup_files(pkg_path: str, main_path: str) -> bool:
"""
备份原始文件
Args:
pkg_path: package.json 文件路径(未使用)
main_path: main.js 文件路径
Returns:
bool: 备份是否成功
"""
try:
# 只备份 main.js
if os.path.exists(main_path):
backup_main = f"{main_path}.bak"
shutil.copy2(main_path, backup_main)
logger.info(f"已备份 main.js: {backup_main}")
return True
except Exception as e:
logger.error(f"备份文件失败: {str(e)}")
return False
def restore_backup_files(pkg_path: str, main_path: str) -> bool:
"""
恢复备份文件
Args:
pkg_path: package.json 文件路径(未使用)
main_path: main.js 文件路径
Returns:
bool: 恢复是否成功
"""
try:
# 只恢复 main.js
backup_main = f"{main_path}.bak"
if os.path.exists(backup_main):
shutil.copy2(backup_main, main_path)
logger.info(f"已恢复 main.js")
return True
logger.error("未找到备份文件")
return False
except Exception as e:
logger.error(f"恢复备份失败: {str(e)}")
return False
def patch_cursor_get_machine_id(restore_mode=False) -> None:
"""
主函数
Args:
restore_mode: 是否为恢复模式
"""
logger.info("开始执行脚本...")
try:
# 获取路径
pkg_path, main_path = get_cursor_paths()
# 检查系统要求
if not check_system_requirements(pkg_path, main_path):
sys.exit(1)
if restore_mode:
# 恢复备份
if restore_backup_files(pkg_path, main_path):
logger.info("备份恢复完成")
else:
logger.error("备份恢复失败")
return
# 获取版本号
try:
with open(pkg_path, "r", encoding="utf-8") as f:
version = json.load(f)["version"]
logger.info(f"当前 Cursor 版本: {version}")
except Exception as e:
logger.error(f"无法读取版本号: {str(e)}")
sys.exit(1)
# 检查版本
if not version_check(version, min_version="0.45.0"):
logger.error("版本不符合要求(需 >= 0.45.x")
sys.exit(1)
logger.info("版本检查通过,准备修改文件")
# 备份文件
if not backup_files(pkg_path, main_path):
logger.error("文件备份失败,终止操作")
sys.exit(1)
# 修改文件
if not modify_main_js(main_path):
sys.exit(1)
logger.info("脚本执行完成")
except Exception as e:
logger.error(f"执行过程中发生错误: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
patch_cursor_get_machine_id()

351
browser_utils.py Normal file
View File

@@ -0,0 +1,351 @@
from DrissionPage import ChromiumOptions, Chromium
import sys
import os
import logging
from dotenv import load_dotenv
import tempfile
import zipfile
import string
load_dotenv()
PROXY_HOST = "h464.kdltpspro.com"
PROXY_PORT = "15818"
PROXY_USERNAME = "h464" # 代理用户名
PROXY_PASSWORD = "kdltpspro" # 代理密码
PROXY_URL = f"http://{PROXY_HOST}:{PROXY_PORT}"
class BrowserManager:
def __init__(self):
self.browser = None
self.current_proxy_info = None
def create_proxyauth_extension(self, proxy_host, proxy_port, proxy_username, proxy_password, scheme='http', plugin_folder=None):
"""
创建Chrome代理认证插件
"""
if plugin_folder is None:
# 创建插件在脚本所在目录
current_directory = os.path.dirname(os.path.abspath(__file__))
plugin_folder = os.path.join(current_directory, 'kdl_Chromium_Proxy')
# 确保文件夹存在
if not os.path.exists(plugin_folder):
os.makedirs(plugin_folder)
logging.info(f"创建代理插件,路径: {plugin_folder}")
manifest_json = """
{
"version": "1.0.0",
"manifest_version": 2,
"name": "kdl_Chromium_Proxy",
"permissions": [
"proxy",
"tabs",
"unlimitedStorage",
"storage",
"<all_urls>",
"webRequest",
"webRequestBlocking",
"browsingData"
],
"background": {
"scripts": ["background.js"]
},
"minimum_chrome_version":"22.0.0"
}
"""
background_js = string.Template("""
var config = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "${scheme}",
host: "${host}",
port: parseInt(${port})
},
bypassList: []
}
};
chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});
function callbackFn(details) {
return {
authCredentials: {
username: "${username}",
password: "${password}"
}
};
}
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);
""").substitute(
host=proxy_host,
port=proxy_port,
username=proxy_username,
password=proxy_password,
scheme=scheme,
)
with open(os.path.join(plugin_folder, "manifest.json"), "w") as manifest_file:
manifest_file.write(manifest_json)
with open(os.path.join(plugin_folder, "background.js"), "w") as background_file:
background_file.write(background_js)
return plugin_folder
def get_proxy(self, use_api=False, mode=False, proxy_choice=None, custom_api=None):
"""
获取代理配置
Args:
use_api: 是否使用API获取代理
mode: 如果开启 返回格式要改变成 host port username password
proxy_choice: 代理选择 (1=本地代理, 2=全局代理)
custom_api: 自定义代理API地址
Returns:
str 或 tuple: 代理URL或(host, port, username, password)元组
"""
logging.info(f"获取代理配置, use_api={use_api}, mode={mode}, proxy_choice={proxy_choice}")
if use_api:
try:
# 从API获取代理
import requests
logging.info("正在从API获取代理...")
# 根据选择使用不同的API
if custom_api:
api_url = custom_api
logging.info(f"使用自定义API: {api_url}")
elif proxy_choice == 1:
api_url = "https://cursorapi.nosqli.com/admin/api.proxyinfo/getproxyarmybendi"
logging.info("使用本地代理API")
elif proxy_choice == 2 or proxy_choice is None: # 如果未指定或选择全局代理
api_url = "https://cursorapi.nosqli.com/admin/api.GlobalProxyip/get_proxy"
logging.info("使用全局代理API")
else:
logging.warning(f"未知的代理选择: {proxy_choice}使用cn代理")
api_url = "https://cursorapi.nosqli.com/admin/api.proxyinfo/getproxyarmybendi"
response = requests.get(api_url)
logging.info(f"API响应状态码: {response.status_code}")
if response.status_code == 200:
proxy_data = response.json()
logging.info(f"API返回数据: {proxy_data}")
if proxy_data.get("code") == 0:
proxy_info = proxy_data.get("data", {})
proxy_host = proxy_info.get("host")
proxy_port = proxy_info.get("port")
proxy_username = proxy_info.get("username", "")
proxy_password = proxy_info.get("password", "")
proxy_data = proxy_info.get("proxy_data", "")
logging.info(f"解析到代理信息 - host:{proxy_host}, port:{proxy_port}")
if proxy_host and proxy_port:
if mode:
logging.info(f"使用API代理(mode=True): 返回元组格式")
return proxy_host, proxy_port, proxy_username, proxy_password, proxy_data
else:
proxy_url = f"http://{proxy_host}:{proxy_port}"
logging.info(f"使用API代理: {proxy_url}")
return proxy_url
logging.warning("从API获取代理失败,使用默认代理配置")
# 使用默认代理
if mode:
# 使用环境变量或常量中的默认值
proxy_host = os.getenv("PROXY_HOST", PROXY_HOST)
proxy_port = os.getenv("PROXY_PORT", PROXY_PORT)
proxy_username = os.getenv("PROXY_USERNAME", PROXY_USERNAME )
proxy_password = os.getenv("PROXY_PASSWORD", PROXY_PASSWORD)
proxy_data = os.getenv("PROXY_DATA", "")
logging.info(f"使用默认代理信息(mode=True): host={proxy_host}, port={proxy_port}")
return proxy_host, proxy_port, proxy_username, proxy_password, proxy_data
else:
default_proxy = os.getenv("BROWSER_PROXY", PROXY_URL)
logging.info(f"使用默认代理: {default_proxy}")
return default_proxy
except Exception as e:
logging.error(f"获取代理出错: {str(e)}")
# 发生异常时使用默认代理
if mode:
proxy_host = os.getenv("PROXY_HOST", PROXY_HOST)
proxy_port = os.getenv("PROXY_PORT", PROXY_PORT)
proxy_username = os.getenv("PROXY_USERNAME", PROXY_USERNAME)
proxy_password = os.getenv("PROXY_PASSWORD", PROXY_PASSWORD)
proxy_data = os.getenv("PROXY_DATA", "")
logging.info(f"异常情况下使用默认代理信息(mode=True)")
return proxy_host, proxy_port, proxy_username, proxy_password, proxy_data
else:
default_proxy = os.getenv("BROWSER_PROXY", PROXY_URL)
logging.info(f"使用默认代理: {default_proxy}")
return default_proxy
# 不使用API时返回默认配置
if mode:
proxy_host = os.getenv("PROXY_HOST", PROXY_HOST)
proxy_port = os.getenv("PROXY_PORT", PROXY_PORT)
proxy_username = os.getenv("PROXY_USERNAME", PROXY_USERNAME)
proxy_password = os.getenv("PROXY_PASSWORD", PROXY_PASSWORD)
proxy_data = os.getenv("PROXY_DATA", "")
logging.info(f"不使用API,返回默认代理信息(mode=True)")
return proxy_host, proxy_port, proxy_username, proxy_password, proxy_data
else:
default_proxy = os.getenv("BROWSER_PROXY", PROXY_URL)
logging.info(f"不使用API,直接返回默认代理: {default_proxy}")
return default_proxy
def get_plugin_folder(self):
"""获取代理插件文件夹路径"""
# 使用当前目录下的kdl_Chromium_Proxy文件夹
current_directory = os.path.dirname(os.path.abspath(__file__))
plugin_folder = os.path.join(current_directory, 'kdl_Chromium_Proxy')
# 确保文件夹存在
if not os.path.exists(plugin_folder):
os.makedirs(plugin_folder)
logging.info(f"代理插件文件夹路径: {plugin_folder}")
return plugin_folder
def init_browser(self, user_agent=None, proxy_choice=None, custom_api=None):
"""
初始化浏览器实例
Args:
user_agent: 自定义User-Agent
proxy_choice: 代理选择 (1=本地代理, 2=全局代理)
custom_api: 自定义代理API地址
Returns:
tuple: (browser实例, 代理信息元组)
"""
try:
# 获取代理配置
proxy_info = self.get_proxy(use_api=True, mode=True, proxy_choice=proxy_choice, custom_api=custom_api)
proxy_host, proxy_port, proxy_username, proxy_password, proxy_data = proxy_info
# 创建代理认证插件
plugin_folder = self.create_proxyauth_extension(
proxy_host=proxy_host,
proxy_port=proxy_port,
proxy_username=proxy_username,
proxy_password=proxy_password
)
# 设置浏览器选项
co = ChromiumOptions()
co.set_argument("--hide-crash-restore-bubble")
co.set_pref("credentials_enable_service", False)
# 添加代理插件
co.add_extension(plugin_folder)
# 设置用户代理
if user_agent:
co.set_user_agent(user_agent)
# 设置无头模式
co.headless(True)
# Mac系统特殊处理
if sys.platform == "darwin":
co.set_argument("--no-sandbox")
co.set_argument("--disable-gpu")
# 创建浏览器实例
browser = Chromium(co)
self.browser = browser
self.current_proxy_info = proxy_info
logging.info("浏览器实例创建成功")
return browser, proxy_info
except Exception as e:
logging.error(f"初始化浏览器失败: {str(e)}")
raise
def _get_browser_options(self, user_agent=None):
"""获取浏览器配置"""
co = ChromiumOptions()
try:
extension_path = self._get_extension_path()
co.add_extension(extension_path)
except FileNotFoundError as e:
logging.warning(f"警告: {e}")
co.set_pref("credentials_enable_service", False)
co.set_argument("--hide-crash-restore-bubble")
proxy = self.get_proxy(True,True)
logging.info(f"代理-----: {proxy}")
# 在类的__init__方法中声明self.current_proxy_info = None
if isinstance(proxy, tuple) and len(proxy) == 4:
self.current_proxy_info = proxy
else:
logging.error("代理信息格式错误")
self.current_proxy_info = None
proxy_host, proxy_port, proxy_username, proxy_password = proxy
try:
# 使用类方法
# proxyauth_plugin_folder = BrowserManager.create_proxyauth_extension(
proxyauth_plugin_folder = self.create_proxyauth_extension(
proxy_host=proxy_host,
proxy_port=proxy_port,
proxy_username=proxy_username,
proxy_password=proxy_password
)
# 使用函数返回的插件文件夹路径而不是硬编码路径
logging.info(f"代理插件文件夹路径: {proxyauth_plugin_folder}")
co.add_extension(proxyauth_plugin_folder)
except Exception as e:
logging.error(f"创建代理扩展失败: {str(e)}")
# 如果创建扩展失败,尝试直接设置代理
proxy_url = f"http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}"
co.set_proxy(proxy_url)
logging.info(f"直接设置代理URL: {proxy_url}")
co.auto_port()
if user_agent:
co.set_user_agent(user_agent)
# 设置为有头模式以便观察验证过程
co.headless(True) # 使用有头模式进行测试
# Mac 系统特殊处理
if sys.platform == "darwin":
co.set_argument("--no-sandbox")
co.set_argument("--disable-gpu")
return co
def _get_extension_path(self):
"""获取插件路径"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "turnstilePatch")
if hasattr(sys, "_MEIPASS"):
extension_path = os.path.join(sys._MEIPASS, "turnstilePatch")
if not os.path.exists(extension_path):
raise FileNotFoundError(f"插件不存在: {extension_path}")
return extension_path
def quit(self):
"""关闭浏览器"""
if self.browser:
try:
self.browser.quit()
except:
pass

125
browser_utils_copy.py Normal file
View File

@@ -0,0 +1,125 @@
from DrissionPage import ChromiumOptions, Chromium
import sys
import os
import logging
from dotenv import load_dotenv
load_dotenv()
PROXY_HOST = "h464.kdltpspro.com"
PROXY_PORT = "15818"
PROXY_URL = f"http://{PROXY_HOST}:{PROXY_PORT}"
class BrowserManager:
def __init__(self):
self.browser = None
def get_proxy(self, use_api=False):
"""
获取代理配置
Args:
use_api: 是否使用API获取代理
Returns:
str: 代理URL
"""
# 从日志可以看出,use_api=False,所以直接返回了默认代理配置
# 没有调用API获取新的代理,而是使用了预设的PROXY_URL
# PROXY_URL 定义为 http://h464.kdltpspro.com:15818
logging.info(f"获取代理配置, use_api={use_api}")
if use_api:
try:
# 从API获取代理
import requests
logging.info("正在从API获取代理...")
response = requests.get("http://127.0.0.1:46880/admin/api.proxyinfo/getproxyarmy")
logging.info(f"API响应状态码: {response.status_code}")
if response.status_code == 200:
proxy_data = response.json()
logging.info(f"API返回数据: {proxy_data}")
if proxy_data.get("code") == 0:
proxy_info = proxy_data.get("data", {})
proxy_host = proxy_info.get("host")
proxy_port = proxy_info.get("port")
logging.info(f"解析到代理信息 - host:{proxy_host}, port:{proxy_port}")
if proxy_host and proxy_port:
proxy_url = f"http://{proxy_host}:{proxy_port}"
logging.info(f"使用API代理: {proxy_url}")
return proxy_url
logging.warning("从API获取代理失败,使用默认代理配置")
default_proxy = os.getenv("BROWSER_PROXY", PROXY_URL)
logging.info(f"使用默认代理: {default_proxy}")
return default_proxy
except Exception as e:
logging.error(f"获取代理出错: {str(e)}")
default_proxy = os.getenv("BROWSER_PROXY", PROXY_URL)
logging.info(f"使用默认代理: {default_proxy}")
return default_proxy
# 不使用API时返回默认配置
default_proxy = os.getenv("BROWSER_PROXY", PROXY_URL)
logging.info(f"不使用API,直接返回默认代理: {default_proxy}")
return default_proxy
def init_browser(self, user_agent=None):
"""初始化浏览器"""
co = self._get_browser_options(user_agent)
self.browser = Chromium(co)
return self.browser
def _get_browser_options(self, user_agent=None):
"""获取浏览器配置"""
co = ChromiumOptions()
try:
extension_path = self._get_extension_path()
co.add_extension(extension_path)
except FileNotFoundError as e:
logging.warning(f"警告: {e}")
co.set_pref("credentials_enable_service", False)
co.set_argument("--hide-crash-restore-bubble")
proxy = self.get_proxy(True)
logging.info(f"代理-----: {proxy}")
if proxy:
co.set_proxy(proxy)
co.auto_port()
if user_agent:
co.set_user_agent(user_agent)
# 设置为有头模式以便观察验证过程
co.headless(False) # 使用有头模式进行测试
# Mac 系统特殊处理
if sys.platform == "darwin":
co.set_argument("--no-sandbox")
co.set_argument("--disable-gpu")
return co
def _get_extension_path(self):
"""获取插件路径"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "turnstilePatch")
if hasattr(sys, "_MEIPASS"):
extension_path = os.path.join(sys._MEIPASS, "turnstilePatch")
if not os.path.exists(extension_path):
raise FileNotFoundError(f"插件不存在: {extension_path}")
return extension_path
def quit(self):
"""关闭浏览器"""
if self.browser:
try:
self.browser.quit()
except:
pass

848
cursor_pro_register.py Normal file
View File

@@ -0,0 +1,848 @@
import os
import platform
import json
import sys
from colorama import Fore, Style
from enum import Enum
from typing import Optional
from account_sync import AccountSync
os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0"
import time
import random
import os
from logger import logging
from browser_utils import BrowserManager
from logo import print_logo
from datetime import datetime
import requests
# 定义 EMOJI 字典
EMOJI = {"ERROR": "", "WARNING": "⚠️", "INFO": ""}
class VerificationStatus(Enum):
"""验证状态枚举"""
PASSWORD_PAGE = "@name=password"
CAPTCHA_PAGE = "@data-index=0"
ACCOUNT_SETTINGS = "Account Settings"
class TurnstileError(Exception):
"""Turnstile 验证相关异常"""
pass
def save_screenshot(tab, stage: str, timestamp: bool = True) -> None:
"""
保存页面截图
Args:
tab: 浏览器标签页对象
stage: 截图阶段标识
timestamp: 是否添加时间戳
"""
try:
# 创建 screenshots 目录
screenshot_dir = "screenshots"
if not os.path.exists(screenshot_dir):
os.makedirs(screenshot_dir)
# 生成文件名
if timestamp:
filename = f"turnstile_{stage}_{int(time.time())}.png"
else:
filename = f"turnstile_{stage}.png"
filepath = os.path.join(screenshot_dir, filename)
# 保存截图
tab.get_screenshot(filepath)
logging.debug(f"截图已保存: {filepath}")
except Exception as e:
logging.warning(f"截图保存失败: {str(e)}")
def check_verification_success(tab) -> Optional[VerificationStatus]:
"""
检查验证是否成功
Returns:
VerificationStatus: 验证成功时返回对应状态,失败返回 None
"""
for status in VerificationStatus:
if tab.ele(status.value):
logging.info(f"验证成功 - 已到达{status.name}页面")
return status
return None
def handle_turnstile(tab, max_retries: int = 2, retry_interval: tuple = (1, 2), start_time=None) -> bool:
"""
处理 Turnstile 验证
Args:
tab: 浏览器标签页对象
max_retries: 最大重试次数
retry_interval: 重试间隔时间范围(最小值, 最大值)
start_time: 注册开始时间戳,用于超时检查
Returns:
bool: 验证是否成功
Raises:
TurnstileError: 验证过程中出现异常
"""
logging.info("正在检测 Turnstile 验证...")
# save_screenshot(tab, "start")
retry_count = 0
try:
while retry_count < max_retries:
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时放弃Turnstile验证")
return False
retry_count += 1
logging.debug(f"{retry_count} 次尝试验证")
try:
# 定位验证框元素
challenge_check = (
tab.ele("@id=cf-turnstile", timeout=2)
.child()
.shadow_root.ele("tag:iframe")
.ele("tag:body")
.sr("tag:input")
)
if challenge_check:
logging.info("检测到 Turnstile 验证框,开始处理...")
# 随机延时后点击验证
time.sleep(random.uniform(1, 3))
challenge_check.click()
time.sleep(2)
# 保存验证后的截图
# save_screenshot(tab, "clicked")
# 检查验证结果
if check_verification_success(tab):
logging.info("Turnstile 验证通过")
# save_screenshot(tab, "success")
return True
except Exception as e:
logging.debug(f"当前尝试未成功: {str(e)}")
# 检查是否已经验证成功
if check_verification_success(tab):
return True
# 随机延时后继续下一次尝试
time.sleep(random.uniform(*retry_interval))
# 超出最大重试次数
logging.error(f"验证失败 - 已达到最大重试次数 {max_retries}")
logging.error(
"请前往开源项目查看更多信息https://github.com/chengazhen/cursor-auto-free"
)
# save_screenshot(tab, "failed")
return False
except Exception as e:
error_msg = f"Turnstile 验证过程发生异常: {str(e)}"
logging.error(error_msg)
# save_screenshot(tab, "error")
raise TurnstileError(error_msg)
def get_cursor_session_token(tab, max_attempts=3, retry_interval=2, start_time=None):
"""
获取Cursor会话token带有重试机制
:param tab: 浏览器标签页
:param max_attempts: 最大尝试次数
:param retry_interval: 重试间隔(秒)
:param start_time: 注册开始时间戳,用于超时检查
:return: session token 或 None
"""
logging.info("开始获取cookie")
attempts = 0
while attempts < max_attempts:
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃获取会话令牌")
return None
try:
cookies = tab.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
return cookie["value"].split("%3A%3A")[1]
attempts += 1
if attempts < max_attempts:
logging.warning(
f"{attempts} 次尝试未获取到CursorSessionToken{retry_interval}秒后重试..."
)
time.sleep(retry_interval)
else:
logging.error(
f"已达到最大尝试次数({max_attempts})获取CursorSessionToken失败"
)
except Exception as e:
logging.error(f"获取cookie失败: {str(e)}")
attempts += 1
if attempts < max_attempts:
logging.info(f"将在 {retry_interval} 秒后重试...")
time.sleep(retry_interval)
return None
registration_timeout = 240 # 默认超时时间4分钟
def check_timeout(start_time, timeout=registration_timeout):
"""检查是否超时
Args:
start_time: 开始时间戳
timeout: 超时时间(秒)
Returns:
bool: 是否超时
"""
elapsed_time = time.time() - start_time
if elapsed_time > timeout:
logging.warning(f"⚠️ 注册超时!已经过去 {elapsed_time:.1f} 秒,超过设定的 {timeout} 秒限制")
return True
return False
def sign_up_account(browser, tab, start_time=None):
logging.info("=== 开始注册账号流程 ===")
logging.info(f"正在访问注册页面: {sign_up_url}")
tab.get(sign_up_url)
try:
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
if tab.ele("@name=first_name"):
logging.info("正在填写个人信息...")
tab.actions.click("@name=first_name").input(first_name)
logging.info(f"已输入名字: {first_name}")
time.sleep(random.uniform(1, 3))
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
tab.actions.click("@name=last_name").input(last_name)
logging.info(f"已输入姓氏: {last_name}")
time.sleep(random.uniform(1, 3))
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
tab.actions.click("@name=email").input(account)
logging.info(f"已输入邮箱: {account}")
time.sleep(random.uniform(1, 3))
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
logging.info("提交个人信息...")
tab.actions.click("@type=submit")
except Exception as e:
logging.error(f"注册页面访问失败: {str(e)}")
return False
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
# 进行第一次Turnstile验证如果失败则返回False
if not handle_turnstile(tab, start_time=start_time):
logging.error("第一次Turnstile验证失败停止当前注册流程")
return False
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
try:
if tab.ele("@name=password"):
logging.info("正在设置密码...")
tab.ele("@name=password").input(password)
time.sleep(random.uniform(1, 3))
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
logging.info("提交密码...")
tab.ele("@type=submit").click()
logging.info("密码设置完成,等待系统响应...")
except Exception as e:
logging.error(f"密码设置失败: {str(e)}")
return False
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
if tab.ele("This email is not available."):
logging.error("注册失败:邮箱已被使用")
return False
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
# 进行第二次Turnstile验证如果失败则返回False
if not handle_turnstile(tab, start_time=start_time):
logging.error("第二次Turnstile验证失败停止当前注册流程")
return False
# 检查是否超时
if start_time and check_timeout(start_time):
logging.warning("注册超时,放弃当前注册流程")
return False
while True:
try:
if tab.ele("Account Settings"):
logging.info("注册成功 - 已进入账户设置页面")
break
if tab.ele("@data-index=0"):
logging.info("正在获取邮箱验证码...")
response = requests.get('https://rnemail.nosqli.com/latest_email?recipient=' + account)
if response.status_code == 200:
email_data = response.json()
code = email_data.get('code')
if not code:
logging.error("获取验证码失败")
return False
logging.info(f"成功获取验证码: {code}")
logging.info("正在输入验证码...")
i = 0
for digit in code:
tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3))
i += 1
logging.info("验证码输入完成")
break
else:
logging.error("获取验证码请求失败")
return False
break
except Exception as e:
logging.error(f"验证码处理过程出错: {str(e)}")
return False
# 进行第三次Turnstile验证如果失败则返回False
if not handle_turnstile(tab):
logging.error("第三次Turnstile验证失败但继续执行后续流程")
# 这里我们选择继续执行,因为此时可能已经成功注册
wait_time = random.randint(3, 6)
for i in range(wait_time):
logging.info(f"等待系统处理中... 剩余 {wait_time-i}")
time.sleep(1)
logging.info("正在获取账户信息...")
tab.get(settings_url)
try:
usage_selector = (
"css:div.col-span-2 > div > div > div > div > "
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
)
usage_ele = tab.ele(usage_selector)
if usage_ele:
usage_info = usage_ele.text
total_usage = usage_info.split("/")[-1].strip()
logging.info(f"账户可用额度上限: {total_usage}")
logging.info(
"请使用听泉助手https://cursorapi.nosqli.com/upload/听泉cursor助手v3.5.3.zip "
)
except Exception as e:
logging.error(f"获取账户额度信息失败: {str(e)}")
logging.info("\n=== 注册完成 ===")
account_info = f"Cursor 账号信息:\n邮箱: {account}\n密码: {password}"
logging.info(account_info)
time.sleep(5)
return True
def get_allowed_domains(max_attempts=3, retry_interval=2):
"""获取当前允许的域名列表"""
attempts = 0
while attempts < max_attempts:
try:
response = requests.get('https://rnemail.nosqli.com/allowed_domains/list')
if response.status_code == 200:
# 解析字符串格式的 JSON
return json.loads(response.text) # 使用 json.loads 解析
else:
logging.error(f"获取域名列表失败: {response.status_code}")
return []
except Exception as e:
logging.error(f"请求域名列表时发生错误: {str(e)}")
attempts += 1
if attempts < max_attempts:
logging.info(f"将在 {retry_interval} 秒后重试...")
time.sleep(retry_interval)
return [] # 返回空列表
class EmailGenerator:
def __init__(self,
password="".join(
random.choices(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*",
k=12,
)
),
selected_domain=None,
):
if not selected_domain:
logging.error("没有可用的域名,无法生成邮箱")
raise ValueError("没有可用的域名") # 抛出异常
self.selected_domain = selected_domain # 直接使用传入的域名
self.default_password = password
self.default_first_name = self.generate_random_name()
self.default_last_name = self.generate_random_name()
def generate_random_name(self, length=6):
"""生成随机用户名"""
first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
rest_letters = "".join(
random.choices("abcdefghijklmnopqrstuvwxyz", k=length - 1)
)
return first_letter + rest_letters
def generate_email(self):
"""生成随机邮箱地址"""
# 使用用户的名字和姓氏的首字母生成邮箱前缀
email_prefix = f"{self.default_first_name[0].lower()}{self.default_last_name.lower()}"
# 添加简短的随机数字以增加唯一性
random_suffix = "".join(random.choices("0123456789", k=2)) # 生成2位随机数字
return f"{email_prefix}{random_suffix}@{self.selected_domain}" # 使用选定的域名
def get_account_info(self):
"""获取完整的账号信息"""
return {
"email": self.generate_email(),
"password": self.default_password,
"first_name": self.default_first_name,
"last_name": self.default_last_name,
}
def get_user_agent(proxy_choice=None, custom_api=None):
"""获取user_agent
Args:
proxy_choice: 代理选择 (1=本地代理, 2=全局代理)
custom_api: 自定义代理API地址
"""
browser_manager = None
try:
# 使用JavaScript获取user agent
logging.info("初始化临时浏览器以获取User-Agent...")
browser_manager = BrowserManager()
browser, proxy_info = browser_manager.init_browser(
proxy_choice=proxy_choice,
custom_api=custom_api
)
user_agent = browser.latest_tab.run_js("return navigator.userAgent")
logging.info(f"获取到User-Agent: {user_agent}")
proxy_host, proxy_port, proxy_username, proxy_password = proxy_info
logging.info(f"初始化proxy: {proxy_host}:{proxy_port}")
return user_agent
except Exception as e:
logging.error(f"获取user agent失败: {str(e)}")
return None
finally:
# 确保浏览器实例被正确关闭
if browser_manager:
logging.info("关闭临时浏览器...")
browser_manager.quit()
def print_end_message():
logging.info("\n\n\n\n\n")
logging.info("=" * 30)
logging.info("所有操作已完成")
logging.info("\n=== 获取更多信息 ===")
logging.info("🔥 vx: behikcigar")
logging.info("=" * 30)
logging.info(
"请使用听泉助手https://cursorapi.nosqli.com/upload/听泉cursor助手v3.5.3.zip "
)
if __name__ == "__main__":
print_logo()
browser_manager = None
try:
logging.info("\n=== 初始化程序 ===")
# 选择账号同步API
print("\n请选择账号同步API:")
print("1. 听泉助手池")
print("2. 高质量号池")
while True:
try:
sync_api_choice = int(input("请输入选项 (1 或 2): ").strip())
if sync_api_choice in [1, 2]:
break
else:
print("无效的选项,请重新输入")
except ValueError:
print("请输入有效的数字")
# 根据选择创建AccountSync实例
account_sync = AccountSync(api_choice=sync_api_choice)
# 选择代理模式
print("\n请选择代理模式:")
print("1. 本地代理")
print("2. 全局代理")
print("3. 自定义代理API")
while True:
try:
proxy_choice = int(input("请输入选项 (1、2 或 3): ").strip())
if proxy_choice in [1, 2, 3]:
break
else:
print("无效的选项,请重新输入")
except ValueError:
print("请输入有效的数字")
# 如果选择自定义API获取API地址
custom_api = None
if proxy_choice == 3:
custom_api = input("请输入自定义代理API地址: ").strip()
if not custom_api:
print("API地址不能为空将使用默认全局代理")
proxy_choice = 2
# 将代理选择保存为全局变量,供后续使用
global_proxy_choice = proxy_choice
global_custom_api = custom_api
# 提示用户选择操作模式
print("\n请选择操作模式:")
print("1. 仅重置机器码")
print("2. 单个注册流程")
print("3. 批量注册流程")
while True:
try:
choice = int(input("请输入选项 (1、2 或 3): ").strip())
if choice in [1, 2, 3]:
break
else:
print("无效的选项,请重新输入")
except ValueError:
print("请输入有效的数字")
if choice == 1:
# 仅执行重置机器码
logging.info("机器码重置完成")
print_end_message()
sys.exit(0)
# 获取注册数量
register_count = 1
if choice == 3:
while True:
try:
register_count = int(input("请输入要注册的账号数量: ").strip())
if register_count > 0:
break
else:
print("请输入大于0的数字")
except ValueError:
print("请输入有效的数字")
logging.info("正在初始化浏览器...")
# 获取user_agent传递代理选择参数
user_agent = get_user_agent(proxy_choice=global_proxy_choice, custom_api=global_custom_api)
if not user_agent:
logging.error("获取user agent失败使用默认值")
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
# 剔除user_agent中的"HeadlessChrome"
user_agent = user_agent.replace("HeadlessChrome", "Chrome")
# 获取并打印浏览器的user-agent
# user_agent = browser.latest_tab.run_js("return navigator.userAgent")
logging.info("正在初始化邮箱验证模块...")
email_handler = None # 先初始化为None,后面根据配置创建
logging.info("\n=== 配置信息 ===")
login_url = "https://authenticator.cursor.sh"
sign_up_url = "https://authenticator.cursor.sh/sign-up"
settings_url = "https://www.cursor.com/settings"
# 获取允许的域名
allowed_domains = get_allowed_domains() # 获取允许的域名列表
if not allowed_domains:
logging.error("没有可用的域名,无法进行注册")
sys.exit(1) # 退出程序
# 提示用户选择域名使用方式
print("请选择域名使用方式:")
print("1. 指定域名")
print("2. 随机轮询域名")
while True:
try:
choice = int(input("请输入选项 (1 或 2): ").strip())
if choice in [1, 2]:
break
else:
print("无效的选项,请重新输入")
except ValueError:
print("请输入有效的数字")
selected_domains = []
if choice == 1:
# 用户指定域名
print("可用域名:")
for index, domain in enumerate(allowed_domains):
print(f"{index + 1}. {domain}")
print("请输入要使用的域名索引,以逗号分隔或范围(如 1-20):")
indices = input("例如: 1,2,3 或 1-20").strip().split(",")
selected_domains = []
for index in indices:
if '-' in index:
start, end = index.split('-')
try:
start = int(start)
end = int(end)
selected_domains.extend(allowed_domains[start - 1:end]) # 添加范围内的域名
except (ValueError, IndexError):
logging.error(f"范围输入无效: {index}")
else:
try:
idx = int(index)
selected_domains.append(allowed_domains[idx - 1]) # 添加单个域名
except (ValueError, IndexError):
logging.error(f"索引输入无效: {index}")
else:
# 随机轮询域名
selected_domains = allowed_domains
# 成功账号列表
successful_accounts = []
# 超时账号计数
timeout_accounts = 0
# 配置数量
config_count = len(selected_domains) # 使用用户选择的域名数量
for i in range(register_count):
logging.info(f"\n=== 开始注册第 {i + 1}/{register_count} 个账号 ===")
current_config_index = i % config_count # 当前使用的配置索引
logging.info(f"使用配置 {current_config_index + 1} / {config_count}") # 打印当前使用的配置和总配置
selected_domain = selected_domains[current_config_index] # 选择当前域名
logging.info(f"使用域名: {selected_domain}")
# 记录注册开始时间
registration_start_time = time.time()
# 设置注册超时时间4分钟 = 240秒
registration_timeout = 240
logging.info(f"注册超时设置为 {registration_timeout}")
# 为每个账号创建新的浏览器实例,确保账号纯净
if browser_manager:
logging.info("关闭上一个浏览器实例...")
browser_manager.quit()
logging.info("创建新的浏览器实例...")
browser_manager = BrowserManager()
# 使用选择的代理模式初始化浏览器
browser, proxy_info = browser_manager.init_browser(
user_agent,
proxy_choice=global_proxy_choice,
custom_api=global_custom_api
)
proxy_host, proxy_port, proxy_username, proxy_password, proxy_data = proxy_info
current_proxies = {
'http': f'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}',
'https': f'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'
}
logging.info(f"API将使用相同代理: {proxy_host}:{proxy_port}")
logging.info(f"API将使用相同代理: {proxy_data}")
# 生成邮箱
email_generator = EmailGenerator(selected_domain=selected_domain) # 传递选定的域名
account_info = email_generator.get_account_info() # 获取账号信息
account = account_info['email'] # 使用生成的邮箱
password = account_info['password'] # 使用生成的密码
first_name = account_info['first_name'] # 使用生成的名字
last_name = account_info['last_name'] # 使用生成的姓氏
logging.info(f"生成的邮箱账号: {account}")
auto_update_cursor_auth = True
tab = browser.latest_tab
tab.run_js("try { turnstile.reset() } catch(e) { }")
logging.info("\n=== 开始注册流程 ===")
logging.info(f"正在访问登录页面: {login_url}")
tab.get(login_url)
# 检查是否超时
if check_timeout(registration_start_time):
logging.warning("放弃当前注册,继续下一个账号")
timeout_accounts += 1
continue
if sign_up_account(browser, tab, registration_start_time):
# 检查是否超时
if check_timeout(registration_start_time):
logging.warning("放弃当前注册,继续下一个账号")
timeout_accounts += 1
continue
logging.info("正在获取会话令牌...")
token = get_cursor_session_token(tab, start_time=registration_start_time)
if token:
# 检查是否超时
if check_timeout(registration_start_time):
logging.warning("放弃当前注册,继续下一个账号")
timeout_accounts += 1
continue
logging.info("更新认证信息...")
logging.info("重置机器码...")
logging.info(f"API将使用相同代理: {proxy_host}:{proxy_port}")
# 保存成功注册的账号信息
account_data = {
"email": account,
"password": password,
"token": token,
"register_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"first_name": first_name,
"last_name": last_name,
'user_agent': user_agent,
'proxy_host': proxy_host,
'proxy_port': proxy_port,
'proxy_username': proxy_username,
'proxy_password': proxy_password,
'proxy_data': proxy_data
}
logging.info("更新认证信息...")
# 添加到成功账号列表
successful_accounts.append(account_data)
# # 同步到服务器
if account_sync.sync_account(account_data):
logging.info("账号已成功同步到服务器")
else:
logging.warning("账号同步到服务器失败")
else:
logging.error("获取会话令牌失败,注册流程未完成")
# 计算本次注册用时
registration_elapsed = time.time() - registration_start_time
logging.info(f"本次注册用时: {registration_elapsed:.1f}")
# 注册间隔,避免频率过快
if i < register_count - 1: # 最后一个账号不需要等待
wait_time = random.randint(1, 4)
logging.info(f"等待 {wait_time} 秒后继续下一个注册...")
time.sleep(wait_time)
# 即使注册失败,也关闭当前浏览器实例,保证下一个注册过程使用全新实例
if browser_manager:
logging.info("关闭当前浏览器实例...")
browser_manager.quit()
browser_manager = None
# 打印注册结果统计
logging.info("\n\n=== 注册结果统计 ===")
logging.info(f"总计尝试: {register_count}")
logging.info(f"成功注册: {len(successful_accounts)}")
logging.info(f"超时注册: {timeout_accounts}")
logging.info(f"其他失败: {register_count - len(successful_accounts) - timeout_accounts}")
logging.info("所有成功注册的账号信息已保存到 successful_accounts.json")
print_end_message()
except Exception as e:
logging.error(f"程序执行出现错误: {str(e)}")
import traceback
logging.error(traceback.format_exc())
finally:
# 确保浏览器实例被正确关闭
if browser_manager:
logging.info("清理资源:正在关闭浏览器...")
try:
browser_manager.quit()
logging.info("浏览器已成功关闭")
except Exception as e:
logging.error(f"关闭浏览器时出错: {str(e)}")
logging.info("程序执行完毕")
# 使用消息框代替input函数
try:
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
root.withdraw() # 隐藏主窗口
messagebox.showinfo("程序执行完毕", "程序执行完毕,点击确定退出...")
root.destroy()
except Exception as e:
# 如果tkinter不可用则使用其他方式
import time
logging.info(f"无法显示消息框: {str(e)}")
print("\n程序执行完毕5秒后自动退出...")
time.sleep(5)

View File

@@ -0,0 +1,30 @@
var config = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "http",
host: "60.188.78.8",
port: parseInt(20138)
},
bypassList: []
}
};
chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});
function callbackFn(details) {
return {
authCredentials: {
username: "CC353276",
password: "0F2F15563675"
}
};
}
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);

View File

@@ -0,0 +1,21 @@
{
"version": "1.0.0",
"manifest_version": 2,
"name": "kdl_Chromium_Proxy",
"permissions": [
"proxy",
"tabs",
"unlimitedStorage",
"storage",
"<all_urls>",
"webRequest",
"webRequestBlocking",
"browsingData"
],
"background": {
"scripts": ["background.js"]
},
"minimum_chrome_version":"22.0.0"
}

84
logger.py Normal file
View File

@@ -0,0 +1,84 @@
import logging
import os
from datetime import datetime
# Configure logging
log_dir = "logs"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
class PrefixFormatter(logging.Formatter):
"""自定义格式化器,为 DEBUG 级别日志添加开源项目前缀"""
def format(self, record):
if record.levelno == logging.DEBUG: # 只给 DEBUG 级别添加前缀
record.msg = f"[开源项目https://github.com/chengazhen/cursor-auto-free] {record.msg}"
return super().format(record)
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(
os.path.join(log_dir, f"{datetime.now().strftime('%Y-%m-%d')}.log"),
encoding="utf-8",
),
],
)
# 为文件处理器设置自定义格式化器
for handler in logging.getLogger().handlers:
if isinstance(handler, logging.FileHandler):
handler.setFormatter(
PrefixFormatter("%(asctime)s - %(levelname)s - %(message)s")
)
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(PrefixFormatter("%(message)s"))
# 将控制台处理器添加到日志记录器
logging.getLogger().addHandler(console_handler)
# 打印日志目录所在路径
logging.info(f"Logger initialized, log directory: {os.path.abspath(log_dir)}")
def main_task():
"""
Main task execution function. Simulates a workflow and handles errors.
"""
try:
logging.info("Starting the main task...")
# Simulated task and error condition
if some_condition():
raise ValueError("Simulated error occurred.")
logging.info("Main task completed successfully.")
except ValueError as ve:
logging.error(f"ValueError occurred: {ve}", exc_info=True)
except Exception as e:
logging.error(f"Unexpected error occurred: {e}", exc_info=True)
finally:
logging.info("Task execution finished.")
def some_condition():
"""
Simulates an error condition. Returns True to trigger an error.
Replace this logic with actual task conditions.
"""
return True
if __name__ == "__main__":
# Application workflow
logging.info("Application started.")
main_task()
logging.info("Application exited.")

16
logo.py Normal file
View File

@@ -0,0 +1,16 @@
CURSOR_LOGO = """
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
"""
def print_logo():
print(CURSOR_LOGO)
if __name__ == "__main__":
print_logo()

36458
names-dataset.txt Normal file

File diff suppressed because it is too large Load Diff

150
package.py Normal file
View File

@@ -0,0 +1,150 @@
import os
import sys
import shutil
import subprocess
import platform
import datetime
import argparse
import time
import traceback
def check_pyinstaller():
"""检查是否已安装 PyInstaller如果没有则安装"""
try:
subprocess.run(["pyinstaller", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
print("✅ PyInstaller 已安装")
return True
except (subprocess.SubprocessError, FileNotFoundError):
print("❌ PyInstaller 未安装,正在安装...")
try:
subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"], check=True)
print("✅ PyInstaller 安装成功")
return True
except subprocess.SubprocessError as e:
print(f"❌ PyInstaller 安装失败: {e}")
return False
def create_executable(app_name, keep_previous):
"""使用 PyInstaller 创建可执行文件"""
print(f"开始打包 cursor_pro_register.py 为 {app_name}...")
dist_folder = "dist"
build_folder = "build"
# 如果不保留旧版本则清除dist和build目录
if not keep_previous:
for folder in [dist_folder, build_folder]:
if os.path.exists(folder):
shutil.rmtree(folder)
print(f"已清理 {folder} 文件夹")
else:
# 确保目录存在
os.makedirs(dist_folder, exist_ok=True)
os.makedirs(build_folder, exist_ok=True)
print("将保留先前版本")
# 生成版本标识(用于区分构建文件夹)
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
build_dir = os.path.join(build_folder, f"build_{timestamp}")
# 检查是否存在 spec 文件并删除
if os.path.exists("cursor_pro_register.spec"):
os.remove("cursor_pro_register.spec")
print("已删除旧的 spec 文件")
# 确定输出文件名
output_name = app_name if app_name else "cursor动态账号注册机"
print(f"输出文件名设置为: {output_name}")
# 构建命令
cmd = [
"pyinstaller",
"--onefile", # 创建单个可执行文件
"--name", output_name,
"--clean", # 清理临时文件
"--workpath", build_dir, # 使用唯一的构建目录
# 移除noconsole选项使用控制台模式
"--noupx", # 不使用UPX压缩提高兼容性
"--add-data", f"turnstilePatch{os.pathsep}turnstilePatch", # 添加额外文件
"cursor_pro_register.py"
]
# 过滤空字符串
cmd = [item for item in cmd if item]
print(f"执行命令: {' '.join(cmd)}")
try:
# 运行 PyInstaller
subprocess.run(cmd, check=True)
executable_name = f"{output_name}.exe" if platform.system() == "Windows" else output_name
executable_path = os.path.join("dist", executable_name)
if os.path.exists(executable_path):
print(f"\n✅ 打包成功! 可执行文件位于: {executable_path}")
# 获取文件大小
size_bytes = os.path.getsize(executable_path)
size_mb = size_bytes / (1024 * 1024)
print(f"可执行文件大小: {size_mb:.2f} MB")
return True
else:
print(f"❌ 打包过程可能出错,找不到可执行文件: {executable_path}")
return False
except subprocess.SubprocessError as e:
print(f"❌ 打包失败: {e}")
traceback.print_exc() # 打印详细的错误信息
return False
if __name__ == "__main__":
print("=" * 50)
print("Cursor Pro Register 打包工具")
print("=" * 50)
# 解析命令行参数
parser = argparse.ArgumentParser(description="Cursor Pro Register 打包工具")
parser.add_argument("app_name", nargs="?", default="cursor动态账号注册机", help="应用名称")
parser.add_argument("keep_previous", nargs="?", type=int, default=0, help="是否保留先前版本 (1=是, 0=否)")
try:
args = parser.parse_args()
print(f"接收到的参数: 应用名称=\"{args.app_name}\", 保留旧版本={bool(args.keep_previous)}")
# 检查当前目录是否有 cursor_pro_register.py
if not os.path.exists("cursor_pro_register.py"):
print("❌ 当前目录找不到 cursor_pro_register.py 文件!")
sys.exit(1)
# 检查是否有 turnstilePatch 目录
if not os.path.exists("turnstilePatch"):
print("⚠️ 警告: 找不到 turnstilePatch 目录,打包可能会缺少必要文件")
try:
choice = input("是否继续打包? (y/n): ")
if choice.lower() != 'y':
print("打包已取消")
sys.exit(0)
except Exception:
# 如果input()出错,默认继续
print("⚠️ 无法获取用户输入,默认继续打包...")
# 检查并安装 PyInstaller
if not check_pyinstaller():
print("❌ 无法安装 PyInstaller打包终止")
sys.exit(1)
# 创建可执行文件
if create_executable(args.app_name, bool(args.keep_previous)):
print("\n打包过程完成!")
else:
print("\n打包过程出现错误,请检查上面的日志信息")
except Exception as e:
print(f"❌ 执行过程中出现未预期的错误: {e}")
traceback.print_exc()
print("程序将在5秒后自动退出...")
time.sleep(5)

223
package_spec.py Normal file
View File

@@ -0,0 +1,223 @@
"""
更高级的打包脚本,使用 spec 文件进行更精细的控制
这个脚本会生成一个 .spec 文件并基于它进行打包
"""
import os
import sys
import shutil
import subprocess
import platform
import datetime
import argparse
from pathlib import Path
def install_requirements():
"""安装必要的依赖"""
requirements = ["pyinstaller", "colorama", "requests"]
for req in requirements:
print(f"正在检查 {req}...")
try:
__import__(req)
print(f"{req} 已安装")
except ImportError:
print(f"{req} 未安装,正在安装...")
try:
subprocess.run([sys.executable, "-m", "pip", "install", req], check=True)
print(f"{req} 安装成功")
except subprocess.SubprocessError as e:
print(f"{req} 安装失败: {e}")
return False
return True
def create_spec_file(app_name):
"""创建 .spec 文件"""
print("正在创建 .spec 文件...")
# 获取当前工作目录
current_dir = os.path.abspath(os.path.dirname(__file__))
# 自动检测项目中的所有 Python 文件
python_files = []
for file in os.listdir(current_dir):
if file.endswith('.py') and file != 'package.py' and file != 'package_spec.py':
python_files.append(file)
main_script = 'cursor_pro_register.py'
if main_script not in python_files:
print(f"❌ 找不到主脚本文件 {main_script}!")
return False
# 确保 turnstilePatch 目录存在
turnstile_path = os.path.join(current_dir, 'turnstilePatch')
if not os.path.exists(turnstile_path):
print("⚠️ 警告: 找不到 turnstilePatch 目录,功能可能受限")
# 获取资源文件
datas = []
if os.path.exists(turnstile_path):
datas.append((turnstile_path, 'turnstilePatch'))
# 检查其他可能的资源文件夹
for folder in ['assets', 'config', 'data']:
folder_path = os.path.join(current_dir, folder)
if os.path.exists(folder_path) and os.path.isdir(folder_path):
datas.append((folder_path, folder))
# 确定输出文件名
output_name = app_name if app_name else "cursor动态账号注册机"
print(f"输出文件名设置为: {output_name}")
# 生成 spec 文件内容
spec_content = f"""# -*- mode: python ; coding: utf-8 -*-
import sys
from pathlib import Path
block_cipher = None
# 收集项目文件
a = Analysis(
['{main_script}'],
pathex=['{current_dir}'],
binaries=[],
datas={datas},
hiddenimports=[
'colorama', 'requests', 'json', 'random', 'datetime', 'enum', 'dotenv',
'tkinter', 'tkinter.messagebox', 'tkinter.ttk',
'threading', 'queue', 'urllib', 'urllib.request', 'urllib.parse',
'time', 'sys', 'os', 'traceback', 'logging', 'typing', 're', 'base64',
'platform', 'collections'
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
# 创建可执行文件
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='{output_name}',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=None,
)
"""
spec_file = 'cursor_pro_register.spec'
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
print(f"✅ 已创建 {spec_file}")
return True
def build_from_spec(keep_previous, app_name):
"""从 spec 文件构建可执行文件"""
print("开始从 spec 文件构建可执行文件...")
dist_folder = "dist"
build_folder = "build"
# 如果不保留旧版本则清除dist和build目录
if not keep_previous:
for folder in [dist_folder, build_folder]:
if os.path.exists(folder):
shutil.rmtree(folder)
print(f"已清理 {folder} 文件夹")
else:
# 确保目录存在
os.makedirs(dist_folder, exist_ok=True)
os.makedirs(build_folder, exist_ok=True)
print("将保留先前版本")
# 生成版本标识(用于区分构建文件夹)
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
build_dir = os.path.join(build_folder, f"build_{timestamp}")
# 确定输出文件名
output_name = app_name if app_name else "cursor动态账号注册机"
# 构建命令
cmd = ["pyinstaller", "--workpath", build_dir, "cursor_pro_register.spec", "--clean", "--noupx"]
print(f"执行命令: {' '.join(cmd)}")
try:
# 运行 PyInstaller 使用 spec 文件
subprocess.run(cmd, check=True)
# 检查构建结果
executable_name = f"{output_name}.exe" if platform.system() == "Windows" else output_name
executable_path = os.path.join("dist", executable_name)
if os.path.exists(executable_path):
print(f"\n✅ 打包成功! 可执行文件位于: {executable_path}")
# 获取文件大小
size_bytes = os.path.getsize(executable_path)
size_mb = size_bytes / (1024 * 1024)
print(f"可执行文件大小: {size_mb:.2f} MB")
return True
else:
print(f"❌ 打包过程可能出错,找不到可执行文件: {executable_path}")
return False
except subprocess.SubprocessError as e:
print(f"❌ 打包失败: {e}")
return False
if __name__ == "__main__":
print("=" * 50)
print("Cursor Pro Register 高级打包工具")
print("=" * 50)
# 解析命令行参数
parser = argparse.ArgumentParser(description="Cursor Pro Register 高级打包工具")
parser.add_argument("app_name", nargs="?", default="cursor动态账号注册机", help="应用名称")
parser.add_argument("keep_previous", nargs="?", type=int, default=0, help="是否保留先前版本 (1=是, 0=否)")
args = parser.parse_args()
print(f"接收到的参数: 应用名称=\"{args.app_name}\", 保留旧版本={bool(args.keep_previous)}")
# 安装必要的依赖
if not install_requirements():
print("❌ 安装依赖失败,打包终止")
sys.exit(1)
# 创建 spec 文件
if not create_spec_file(args.app_name):
print("❌ 创建 spec 文件失败,打包终止")
sys.exit(1)
# 从 spec 文件构建
if build_from_spec(bool(args.keep_previous), args.app_name):
print("\n打包过程完成!")
else:
print("\n打包过程出现错误,请检查上面的日志信息")
input("按任意键退出...")

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
DrissionPage==4.1.0.9
colorama==0.4.6
python-dotenv==1.0.0
pyinstaller

169
run.bat Normal file
View File

@@ -0,0 +1,169 @@
@echo off
rem 使用更可靠的方式设置UTF-8编码
chcp 65001 > nul
setlocal enabledelayedexpansion
title Cursor Pro Register 打包工具
cls
echo ================================================
echo Cursor Pro Register 打包工具
echo ================================================
echo.
rem 检测Python是否已安装
python --version > nul 2>&1
if %errorlevel% neq 0 (
echo [错误] 未检测到Python安装请先安装Python 3.8或更高版本
echo 您可以从 https://www.python.org/downloads/ 下载安装
pause
exit /b 1
)
echo [信息] 检测到Python已安装
echo.
rem 获取当前日期和时间
for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "YEAR=%dt:~0,4%"
set "MONTH=%dt:~4,2%"
set "DAY=%dt:~6,2%"
set "TODAY=%YEAR%%MONTH%%DAY%"
rem 默认设置
set "DEFAULT_VERSION=0.1"
set "DEFAULT_NAME=cursor注册机"
set "APP_VERSION=%DEFAULT_VERSION%"
set "APP_NAME=%DEFAULT_NAME%"
set "KEEP_PREVIOUS=1"
:Menu
cls
echo ================================================
echo Cursor Pro Register 打包工具
echo ================================================
echo.
echo [信息] 检测到Python已安装
echo.
rem 显示菜单
echo 请选择打包操作:
echo [1] 快速打包(使用默认设置)
echo [2] 自定义打包设置
echo [3] 高级打包模式使用spec文件
echo [4] 退出
echo.
set /p USER_CHOICE="请选择 [1-4]: "
rem 处理选择
if "%USER_CHOICE%"=="1" (
echo.
echo [信息] 正在使用默认设置进行打包
goto StartPackaging
) else if "%USER_CHOICE%"=="2" (
echo.
echo [信息] 请输入自定义设置
set /p APP_NAME="应用名称 [默认: %DEFAULT_NAME%]: "
if "!APP_NAME!"=="" set "APP_NAME=%DEFAULT_NAME%"
set /p APP_VERSION="版本号 [默认: %DEFAULT_VERSION%]: "
if "!APP_VERSION!"=="" set "APP_VERSION=%DEFAULT_VERSION%"
echo.
echo 是否保留之前的打包版本?
set /p KEEP_CHOICE="是否保留 [Y=是, N=否, 默认=Y]: "
if /i "!KEEP_CHOICE!"=="N" (
set "KEEP_PREVIOUS=0"
) else (
set "KEEP_PREVIOUS=1"
)
goto StartPackaging
) else if "%USER_CHOICE%"=="3" (
echo.
echo [信息] 正在启动高级打包模式
set /p APP_NAME="应用名称 [默认: %DEFAULT_NAME%]: "
if "!APP_NAME!"=="" set "APP_NAME=%DEFAULT_NAME%"
set /p APP_VERSION="版本号 [默认: %DEFAULT_VERSION%]: "
if "!APP_VERSION!"=="" set "APP_VERSION=%DEFAULT_VERSION%"
echo.
echo 是否保留之前的打包版本?
set /p KEEP_CHOICE="是否保留 [Y=是, N=否, 默认=Y]: "
if /i "!KEEP_CHOICE!"=="N" (
set "KEEP_PREVIOUS=0"
) else (
set "KEEP_PREVIOUS=1"
)
echo.
echo [信息] 正在使用高级打包模式...
rem 检查Python脚本是否存在
if not exist package_spec.py (
echo [错误] 找不到package_spec.py文件无法进行高级打包
echo 按任意键返回主菜单...
pause > nul
goto Menu
)
python package_spec.py "!APP_NAME!v!APP_VERSION!" !KEEP_PREVIOUS!
if %errorlevel% neq 0 (
echo [错误] Python脚本执行失败请检查上面的错误信息
)
goto End
) else if "%USER_CHOICE%"=="4" (
echo.
echo [信息] 已取消打包操作
goto End
) else (
echo.
echo [错误] 无效的选择,请重新输入...
timeout /t 2 > nul
goto Menu
)
:StartPackaging
rem 构建最终的应用名称
set "FINAL_NAME=!APP_NAME!v!APP_VERSION!"
echo.
echo [信息] 使用以下设置进行打包:
echo 应用名称: !FINAL_NAME!
echo 保留旧版本: !KEEP_PREVIOUS!
echo.
rem 检查Python脚本是否存在
if not exist package.py (
echo [错误] 找不到package.py文件无法进行打包
echo 按任意键返回主菜单...
pause > nul
goto Menu
)
rem 开始打包
echo [信息] 开始打包过程...
rem 直接调用Python执行打包避免混合输出到控制台的方式
python package.py "!FINAL_NAME!" !KEEP_PREVIOUS!
if %errorlevel% neq 0 (
echo [错误] Python脚本执行失败请检查上面的错误信息
)
:End
echo.
echo ================================================
echo 打包过程已结束
echo ================================================
echo.
echo 是否返回主菜单? [Y=是, N=退出]
set /p MENU_CHOICE="选择 [Y/N]: "
if /i "!MENU_CHOICE!"=="Y" goto Menu
echo 感谢使用Cursor Pro Register打包工具再见!
timeout /t 3 > nul
endlocal

51
run.sh Normal file
View File

@@ -0,0 +1,51 @@
#!/bin/bash
# 设置终端颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
clear
echo -e "${BLUE}================================================${NC}"
echo -e "${BLUE} Cursor Pro Register 打包工具 ${NC}"
echo -e "${BLUE}================================================${NC}"
echo ""
# 检测Python是否已安装
if ! command -v python3 &> /dev/null; then
echo -e "${RED}[错误] 未检测到Python安装请先安装Python 3.8或更高版本${NC}"
echo -e "您可以从 https://www.python.org/downloads/ 下载安装"
echo ""
read -p "按回车键退出..."
exit 1
fi
echo -e "${GREEN}[信息] 检测到Python已安装${NC}"
echo ""
echo "请选择打包模式:"
echo "1. 基本打包模式(简单快速)"
echo "2. 高级打包模式(自定义配置)"
echo ""
read -p "请选择 [1,2]: " choice
if [[ $choice == "1" ]]; then
echo ""
echo -e "${GREEN}[信息] 已选择基本打包模式${NC}"
# 确保脚本有执行权限
chmod +x package.py
python3 package.py
else
echo ""
echo -e "${GREEN}[信息] 已选择高级打包模式${NC}"
# 确保脚本有执行权限
chmod +x package_spec.py
python3 package_spec.py
fi
echo ""
echo -e "${BLUE}================================================${NC}"
echo -e "${GREEN}打包过程已结束${NC}"
echo -e "${BLUE}================================================${NC}"
read -p "按回车键退出..."

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -0,0 +1,18 @@
{
"manifest_version": 3,
"name": "Turnstile Patcher",
"version": "2.1",
"content_scripts": [
{
"js": [
"./script.js"
],
"matches": [
"<all_urls>"
],
"run_at": "document_start",
"all_frames": true,
"world": "MAIN"
}
]
}

View File

@@ -0,0 +1 @@

12
turnstilePatch/script.js Normal file
View File

@@ -0,0 +1,12 @@
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// old method wouldn't work on 4k screens
let screenX = getRandomInt(800, 1200);
let screenY = getRandomInt(400, 600);
Object.defineProperty(MouseEvent.prototype, 'screenX', { value: screenX });
Object.defineProperty(MouseEvent.prototype, 'screenY', { value: screenY });