3 Commits
main ... v3

16 changed files with 1474 additions and 3 deletions

View File

@@ -6,6 +6,7 @@ import requests
from typing import Dict, Optional from typing import Dict, Optional
from config import Config from config import Config
from logger import logging from logger import logging
import patch_cursor_get_machine_id
class AccountManager: class AccountManager:
def __init__(self, api_base_url: str = None): def __init__(self, api_base_url: str = None):
@@ -69,6 +70,25 @@ class AccountManager:
"Authorization": f"Bearer {self.api_token}" "Authorization": f"Bearer {self.api_token}"
} }
# 确保machine_id不为空
machine_id = account_info.get("machine_id")
if not machine_id:
logging.warning("machine_id为空尝试重新获取")
try:
# 先执行patch操作
patch_cursor_get_machine_id.patch_cursor_get_machine_id()
# 然后获取machine_id
pkg_path, _ = patch_cursor_get_machine_id.get_cursor_paths()
with open(pkg_path, "r", encoding="utf-8") as f:
pkg_data = json.load(f)
machine_id = pkg_data.get("machineId", "")
if machine_id:
logging.info(f"成功获取machine_id: {machine_id}")
except Exception as e:
logging.error(f"获取machine_id失败: {str(e)}")
machine_id = ""
# 准备请求数据 # 准备请求数据
data = { data = {
"email": account_info["email"], "email": account_info["email"],
@@ -77,7 +97,7 @@ class AccountManager:
"last_name": account_info["last_name"], "last_name": account_info["last_name"],
"access_token": account_info["access_token"], "access_token": account_info["access_token"],
"refresh_token": account_info["refresh_token"], "refresh_token": account_info["refresh_token"],
"machine_id": account_info.get("machine_id", ""), "machine_id": machine_id,
"user_agent": account_info.get("user_agent", ""), "user_agent": account_info.get("user_agent", ""),
"registration_time": datetime.fromisoformat(account_info["registration_time"]).strftime("%Y-%m-%d %H:%M:%S") "registration_time": datetime.fromisoformat(account_info["registration_time"]).strftime("%Y-%m-%d %H:%M:%S")
} }
@@ -91,7 +111,7 @@ class AccountManager:
) )
response_data = response.json() response_data = response.json()
if response_data["code"] == 0: if response_data["code"] == 0 and "添加成功" in response_data.get("msg", ""):
logging.info(f"账号信息已同步到服务器: {account_info['email']}") logging.info(f"账号信息已同步到服务器: {account_info['email']}")
return True return True
elif response_data["code"] == 400: elif response_data["code"] == 400:

View File

@@ -71,6 +71,162 @@
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T12:43:36.366349", "registration_time": "2025-02-11T12:43:36.366349",
"created_at": "2025-02-11T12:43:36.367348" "created_at": "2025-02-11T12:43:36.367348"
},
{
"email": "updxuihl249936@nosqli.com",
"password": "565zgKZbqe7i",
"first_name": "Pvnoyo",
"last_name": "Zwisgk",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUDI0UkVIQ1M2RkI1NlJOQjQwVkNYIiwidGltZSI6IjE3MzkyNTAwNTkiLCJyYW5kb21uZXNzIjoiYWI4NWQ4ZGEtNWRjOC00OGFjIiwiZXhwIjo0MzMxMjUwMDU5LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.HgDqpySIEFX-GwWhP-oVOGbHlkK5pjweN-RrTg_Xlck",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUDI0UkVIQ1M2RkI1NlJOQjQwVkNYIiwidGltZSI6IjE3MzkyNTAwNTkiLCJyYW5kb21uZXNzIjoiYWI4NWQ4ZGEtNWRjOC00OGFjIiwiZXhwIjo0MzMxMjUwMDU5LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.HgDqpySIEFX-GwWhP-oVOGbHlkK5pjweN-RrTg_Xlck",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T13:01:36.672278",
"created_at": "2025-02-11T13:01:36.672278"
},
{
"email": "jrxqnsoz250264@nosqli.com",
"password": "q5^3wK$7EDSI",
"first_name": "Izklau",
"last_name": "Buaxxr",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUEJLQlIxOTNKMjY3RENSVDRTR1YyIiwidGltZSI6IjE3MzkyNTAzNDgiLCJyYW5kb21uZXNzIjoiYWIyNWVhYTYtNDQzZC00Y2Q0IiwiZXhwIjo0MzMxMjUwMzQ4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.f3VIttCJLWqhkEZpPmWJlYw32FuV_gLWl9E0N-O9oIc",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T13:06:24.503645",
"created_at": "2025-02-11T13:06:24.504644"
},
{
"email": "geagvufj250945@nosqli.com",
"password": "*iBcNX@@Lc5r",
"first_name": "Txnrko",
"last_name": "Glkaro",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUTBCQjRNWjFUSDFCNDRUODhZRTkyIiwidGltZSI6IjE3MzkyNTEwNDMiLCJyYW5kb21uZXNzIjoiYThhMjlhY2YtMmFhMy00Zjk4IiwiZXhwIjo0MzMxMjUxMDQzLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.xtigV1ORDxmYvM37Ejj_bTwoRNwT_UFaepiNQiBT0wc",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUTBCQjRNWjFUSDFCNDRUODhZRTkyIiwidGltZSI6IjE3MzkyNTEwNDMiLCJyYW5kb21uZXNzIjoiYThhMjlhY2YtMmFhMy00Zjk4IiwiZXhwIjo0MzMxMjUxMDQzLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.xtigV1ORDxmYvM37Ejj_bTwoRNwT_UFaepiNQiBT0wc",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T13:19:48.702686",
"created_at": "2025-02-11T13:19:48.702686"
},
{
"email": "vuipeqkn251224@586vip.cn",
"password": "yb08AB@nnFk%",
"first_name": "Zyepdz",
"last_name": "Oexxrl",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUThWVDY4UURRR01GQjBTTlEwOTBNIiwidGltZSI6IjE3MzkyNTEzMjciLCJyYW5kb21uZXNzIjoiZmU0YzQxNWEtYTQ0NC00YjFhIiwiZXhwIjo0MzMxMjUxMzI3LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.zq0Gn8ujbdKv-LETdRIxYc8aw5i4bAfmP-eY-mkRS64",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUThWVDY4UURRR01GQjBTTlEwOTBNIiwidGltZSI6IjE3MzkyNTEzMjciLCJyYW5kb21uZXNzIjoiZmU0YzQxNWEtYTQ0NC00YjFhIiwiZXhwIjo0MzMxMjUxMzI3LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.zq0Gn8ujbdKv-LETdRIxYc8aw5i4bAfmP-eY-mkRS64",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T13:24:29.297014",
"created_at": "2025-02-11T13:24:29.297014"
},
{
"email": "xxugkslr251488@586vip.cn",
"password": "yb08AB@nnFk%",
"first_name": "Ttdkpi",
"last_name": "Lqevwq",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUUc5Q0NWUDNZU0REVDEwQzBLS1FLIiwidGltZSI6IjE3MzkyNTE1NjciLCJyYW5kb21uZXNzIjoiNWQwYjY4YTgtYWNkZi00ZDc4IiwiZXhwIjo0MzMxMjUxNTY3LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.ypgfQ0aQu268HHhdFPTnHp7IHcSr1WQarxlCpgeErTI",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktTUUc5Q0NWUDNZU0REVDEwQzBLS1FLIiwidGltZSI6IjE3MzkyNTE1NjciLCJyYW5kb21uZXNzIjoiNWQwYjY4YTgtYWNkZi00ZDc4IiwiZXhwIjo0MzMxMjUxNTY3LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.ypgfQ0aQu268HHhdFPTnHp7IHcSr1WQarxlCpgeErTI",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T13:26:42.615884",
"created_at": "2025-02-11T13:26:42.615884"
},
{
"email": "fzbsolew265285@586vip.cn",
"password": "HdI@COBPCO9x",
"first_name": "Kdlynp",
"last_name": "Omcope",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNE45VFowOUc3R1haMDhHVlgxQzZYIiwidGltZSI6IjE3MzkyNjUzNDMiLCJyYW5kb21uZXNzIjoiOTRiZWM5YTctOWYyMi00NGFhIiwiZXhwIjo0MzMxMjY1MzQzLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.ZnYwmu8RF4BvEaH7ZFWgnzgn6ZIBMHxKqS2-ZQu7W-0",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNE45VFowOUc3R1haMDhHVlgxQzZYIiwidGltZSI6IjE3MzkyNjUzNDMiLCJyYW5kb21uZXNzIjoiOTRiZWM5YTctOWYyMi00NGFhIiwiZXhwIjo0MzMxMjY1MzQzLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.ZnYwmu8RF4BvEaH7ZFWgnzgn6ZIBMHxKqS2-ZQu7W-0",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T17:16:21.905458",
"created_at": "2025-02-11T17:16:21.905458"
},
{
"email": "chvldgvz265405@586vip.cn",
"password": "HdI@COBPCO9x",
"first_name": "Nsgtsc",
"last_name": "Yrgcuy",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNFJYODE0WVhQMlpaWkpENk1aUlI1IiwidGltZSI6IjE3MzkyNjU0NjIiLCJyYW5kb21uZXNzIjoiZDJiNDAzMDQtNTcyNy00ZTg5IiwiZXhwIjo0MzMxMjY1NDYyLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.dBB1AiYk06INf_YnN8Kuu1ZgI9kUi5eDqWz4C2ExfQg",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNFJYODE0WVhQMlpaWkpENk1aUlI1IiwidGltZSI6IjE3MzkyNjU0NjIiLCJyYW5kb21uZXNzIjoiZDJiNDAzMDQtNTcyNy00ZTg5IiwiZXhwIjo0MzMxMjY1NDYyLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.dBB1AiYk06INf_YnN8Kuu1ZgI9kUi5eDqWz4C2ExfQg",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T17:18:16.076468",
"created_at": "2025-02-11T17:18:16.076468"
},
{
"email": "skdjndzg265518@586vip.cn",
"password": "HdI@COBPCO9x",
"first_name": "Suykzr",
"last_name": "Fomxgs",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNFdCWENBVFpCMkcwQkFCMlExQUdDIiwidGltZSI6IjE3MzkyNjU1NzQiLCJyYW5kb21uZXNzIjoiZTU1Mzk3YjgtYzU5Ny00ZWIwIiwiZXhwIjo0MzMxMjY1NTc0LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.bIrBvtQxgV8tiD0MK8ZkdBnvoCvWcmGWbMzXyJo__-Q",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNFdCWENBVFpCMkcwQkFCMlExQUdDIiwidGltZSI6IjE3MzkyNjU1NzQiLCJyYW5kb21uZXNzIjoiZTU1Mzk3YjgtYzU5Ny00ZWIwIiwiZXhwIjo0MzMxMjY1NTc0LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.bIrBvtQxgV8tiD0MK8ZkdBnvoCvWcmGWbMzXyJo__-Q",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T17:20:12.770495",
"created_at": "2025-02-11T17:20:12.770495"
},
{
"email": "qgmjlglr265641@586vip.cn",
"password": "HdI@COBPCO9x",
"first_name": "Ozyepu",
"last_name": "Nyrnjw",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTA1Vk5WOFE4Mko1OERKQlc0NFFKIiwidGltZSI6IjE3MzkyNjU2OTkiLCJyYW5kb21uZXNzIjoiOWQ2Y2NjYTMtYTU4Yy00OWI0IiwiZXhwIjo0MzMxMjY1Njk5LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.cuR7IJ2SDyvWHzdXk6wfRDL1L2AH38AIs1hweN49u2s",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTA1Vk5WOFE4Mko1OERKQlc0NFFKIiwidGltZSI6IjE3MzkyNjU2OTkiLCJyYW5kb21uZXNzIjoiOWQ2Y2NjYTMtYTU4Yy00OWI0IiwiZXhwIjo0MzMxMjY1Njk5LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.cuR7IJ2SDyvWHzdXk6wfRDL1L2AH38AIs1hweN49u2s",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T17:22:17.167933",
"created_at": "2025-02-11T17:22:17.167933"
},
{
"email": "cetkjbyo265764@586vip.cn",
"password": "HdI@COBPCO9x",
"first_name": "Ykaokw",
"last_name": "Xgnibi",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTNXUUtRRkY1VEpQNTJQV1o1V1k2IiwidGltZSI6IjE3MzkyNjU4MjEiLCJyYW5kb21uZXNzIjoiYThkNTZiODgtYTQ2OS00NDY0IiwiZXhwIjo0MzMxMjY1ODIxLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.2VcD_cjuI1V6775SrHuJEPKMJz094yRywVC-SdgKMl8",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTNXUUtRRkY1VEpQNTJQV1o1V1k2IiwidGltZSI6IjE3MzkyNjU4MjEiLCJyYW5kb21uZXNzIjoiYThkNTZiODgtYTQ2OS00NDY0IiwiZXhwIjo0MzMxMjY1ODIxLCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.2VcD_cjuI1V6775SrHuJEPKMJz094yRywVC-SdgKMl8",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T17:24:20.739727",
"created_at": "2025-02-11T17:24:20.739727"
},
{
"email": "vosglsoj265881@586vip.cn",
"password": "HdI@COBPCO9x",
"first_name": "Tbkztl",
"last_name": "Cpcnyd",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTdGMjQ3QktYNlFHRlpTNjVOOFBXIiwidGltZSI6IjE3MzkyNjU5MzgiLCJyYW5kb21uZXNzIjoiOWIzNjdmN2QtNmRkMy00ZGNkIiwiZXhwIjo0MzMxMjY1OTM4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.6hR-fP769x368tTlBjmtIK0ZpHeJOE9Y4FynxAr48bI",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNTdGMjQ3QktYNlFHRlpTNjVOOFBXIiwidGltZSI6IjE3MzkyNjU5MzgiLCJyYW5kb21uZXNzIjoiOWIzNjdmN2QtNmRkMy00ZGNkIiwiZXhwIjo0MzMxMjY1OTM4LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.6hR-fP769x368tTlBjmtIK0ZpHeJOE9Y4FynxAr48bI",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T17:26:21.044597",
"created_at": "2025-02-11T17:26:21.044597"
},
{
"email": "otzumrrm266001@586vip.cn",
"password": "HdI@COBPCO9x",
"first_name": "Mfmwza",
"last_name": "Axmmui",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNUIzNEM2OEc4Szc1NlM2SE5YMFIyIiwidGltZSI6IjE3MzkyNjYwNTUiLCJyYW5kb21uZXNzIjoiYmM0NWIyZTYtNjgxMS00OTE4IiwiZXhwIjo0MzMxMjY2MDU1LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.geHcjT0DzddtOpxILQTawKvc90_dqJG6c-_ZDS9ZVUw",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNUIzNEM2OEc4Szc1NlM2SE5YMFIyIiwidGltZSI6IjE3MzkyNjYwNTUiLCJyYW5kb21uZXNzIjoiYmM0NWIyZTYtNjgxMS00OTE4IiwiZXhwIjo0MzMxMjY2MDU1LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.geHcjT0DzddtOpxILQTawKvc90_dqJG6c-_ZDS9ZVUw",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T17:28:13.605804",
"created_at": "2025-02-11T17:28:13.605804"
},
{
"email": "nrnivqap266115@nosqli.com",
"password": "HdI@COBPCO9x",
"first_name": "Veqlgf",
"last_name": "Qwxxdc",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNUVGQlk0RDRZV1paWTVSNUpFUjhYIiwidGltZSI6IjE3MzkyNjYxNjYiLCJyYW5kb21uZXNzIjoiMjM4YzM5MGEtY2U4Mi00ZDQ2IiwiZXhwIjo0MzMxMjY2MTY2LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.fPCSlBZR8ZoKKfij6NYRz0lDvIFm3Rrs2KZMGJIyWAE",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhdXRoMHx1c2VyXzAxSktUNUVGQlk0RDRZV1paWTVSNUpFUjhYIiwidGltZSI6IjE3MzkyNjYxNjYiLCJyYW5kb21uZXNzIjoiMjM4YzM5MGEtY2U4Mi00ZDQ2IiwiZXhwIjo0MzMxMjY2MTY2LCJpc3MiOiJodHRwczovL2F1dGhlbnRpY2F0aW9uLmN1cnNvci5zaCIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJhdWQiOiJodHRwczovL2N1cnNvci5jb20ifQ.fPCSlBZR8ZoKKfij6NYRz0lDvIFm3Rrs2KZMGJIyWAE",
"machine_id": "",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
"registration_time": "2025-02-11T17:30:05.829510",
"created_at": "2025-02-11T17:30:05.829510"
} }
] ]
} }

View File

@@ -0,0 +1,538 @@
import os
import json
import requests
import logging
import subprocess
import uuid
import hashlib
import sys
import time
from typing import Optional, Dict, Tuple
from pathlib import Path
from utils.config import Config
from utils.cursor_registry import CursorRegistry
from cursor_auth_manager import CursorAuthManager
def get_hardware_id() -> str:
"""获取硬件唯一标识"""
try:
# 获取CPU信息
cpu_info = subprocess.check_output('wmic cpu get ProcessorId').decode()
cpu_id = cpu_info.split('\n')[1].strip()
# 获取主板序列号
board_info = subprocess.check_output('wmic baseboard get SerialNumber').decode()
board_id = board_info.split('\n')[1].strip()
# 获取BIOS序列号
bios_info = subprocess.check_output('wmic bios get SerialNumber').decode()
bios_id = bios_info.split('\n')[1].strip()
# 组合信息并生成哈希
combined = f"{cpu_id}:{board_id}:{bios_id}"
return hashlib.md5(combined.encode()).hexdigest()
except Exception as e:
logging.error(f"获取硬件ID失败: {str(e)}")
# 如果获取失败使用UUID作为备选方案
return str(uuid.uuid4())
class AccountSwitcher:
def __init__(self):
self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
self.app_path = self.cursor_path / "resources" / "app"
self.package_json = self.app_path / "package.json"
self.auth_manager = CursorAuthManager()
self.config = Config()
self.hardware_id = get_hardware_id()
self.registry = CursorRegistry() # 添加注册表操作工具类
def get_device_info(self) -> dict:
"""获取设备信息"""
try:
import platform
import socket
import requests
# 获取操作系统信息
os_info = f"{platform.system()} {platform.version()}"
# 获取设备名称
device_name = platform.node()
# 获取地理位置(可选)
try:
ip_info = requests.get('https://ipapi.co/json/', timeout=5).json()
location = f"{ip_info.get('country_name', '')}-{ip_info.get('region', '')}-{ip_info.get('city', '')}"
except:
location = ""
return {
"os": os_info,
"device_name": device_name,
"location": location
}
except Exception as e:
logging.error(f"获取设备信息失败: {str(e)}")
return {
"os": "Windows 10",
"device_name": "未知设备",
"location": ""
}
def check_activation_code(self, code: str) -> Tuple[bool, str, Optional[Dict]]:
"""验证激活码
Returns:
Tuple[bool, str, Optional[Dict]]: (是否成功, 提示消息, 账号信息)
"""
try:
# 获取当前状态
member_info = self.config.load_member_info()
# 分割多个激活码
codes = [c.strip() for c in code.split(',')]
success_codes = []
failed_codes = []
activation_results = []
# 获取设备信息
device_info = self.get_device_info()
# 逐个验证激活码
for single_code in codes:
if not single_code:
continue
# 验证激活码
endpoint = "https://cursorapi.nosqli.com/admin/api.member/activate"
data = {
"code": single_code,
"machine_id": self.hardware_id,
"os": device_info["os"],
"device_name": device_info["device_name"],
"location": device_info["location"]
}
headers = {
"Content-Type": "application/json"
}
try:
response = requests.post(
endpoint,
json=data,
headers=headers,
timeout=10
)
response_data = response.json()
if response_data.get("code") == 200:
result_data = response_data.get("data", {})
logging.info(f"激活码 {single_code} 验证成功: {response_data.get('msg', '')}")
activation_results.append(result_data)
success_codes.append(single_code)
elif response_data.get("code") == 400:
error_msg = response_data.get("msg", "参数错误")
if "已被使用" in error_msg or "已激活" in error_msg:
logging.warning(f"激活码 {single_code} 已被使用")
failed_codes.append(f"{single_code} (已被使用)")
else:
logging.error(f"激活码 {single_code} 验证失败: {error_msg}")
failed_codes.append(f"{single_code} ({error_msg})")
elif response_data.get("code") == 500:
error_msg = response_data.get("msg", "系统错误")
logging.error(f"激活码 {single_code} 验证失败: {error_msg}")
failed_codes.append(f"{single_code} ({error_msg})")
else:
error_msg = response_data.get("msg", "未知错误")
logging.error(f"激活码 {single_code} 验证失败: {error_msg}")
failed_codes.append(f"{single_code} ({error_msg})")
except requests.RequestException as e:
logging.error(f"激活码 {single_code} 请求失败: {str(e)}")
failed_codes.append(f"{single_code} (网络请求失败)")
except Exception as e:
logging.error(f"激活码 {single_code} 处理失败: {str(e)}")
failed_codes.append(f"{single_code} (处理失败)")
if not success_codes:
failed_msg = "\n".join(failed_codes)
return False, f"激活失败:\n{failed_msg}", None
try:
# 使用最后一次激活的结果作为最终状态
final_result = activation_results[-1]
# 保存会员信息
member_info = {
"hardware_id": final_result.get("machine_id", self.hardware_id),
"expire_time": final_result.get("expire_time", ""),
"days_left": final_result.get("days_left", 0), # 使用days_left
"total_days": final_result.get("total_days", 0), # 使用total_days
"status": final_result.get("status", "inactive"),
"device_info": final_result.get("device_info", device_info),
"activation_time": final_result.get("activation_time", ""),
"activation_records": final_result.get("activation_records", []) # 保存激活记录
}
self.config.save_member_info(member_info)
# 生成结果消息
message = f"激活成功\n"
# 显示每个成功激活码的信息
for i, result in enumerate(activation_results, 1):
message += f"\n{i}个激活码:\n"
message += f"- 新增天数: {result.get('added_days', 0)}\n" # 使用added_days显示本次新增天数
# 格式化时间显示
activation_time = result.get('activation_time', '')
if activation_time:
try:
from datetime import datetime
dt = datetime.strptime(activation_time, "%Y-%m-%d %H:%M:%S")
activation_time = dt.strftime("%Y-%m-%d %H:%M:%S")
except:
pass
message += f"- 激活时间: {activation_time}\n"
message += f"\n最终状态:"
message += f"\n- 总天数: {final_result.get('total_days', 0)}" # 累计总天数
message += f"\n- 剩余天数: {final_result.get('days_left', 0)}" # 剩余天数
# 格式化到期时间显示
expire_time = final_result.get('expire_time', '')
if expire_time:
try:
dt = datetime.strptime(expire_time, "%Y-%m-%d %H:%M:%S")
expire_time = dt.strftime("%Y-%m-%d %H:%M:%S")
except:
pass
message += f"\n- 到期时间: {expire_time}"
# 显示激活记录历史
message += "\n\n历史激活记录:"
for record in final_result.get('activation_records', []):
activation_time = record.get('activation_time', '')
if activation_time:
try:
dt = datetime.strptime(activation_time, "%Y-%m-%d %H:%M:%S")
activation_time = dt.strftime("%Y-%m-%d %H:%M:%S")
except:
pass
message += f"\n- 激活码: {record.get('code', '')}"
message += f"\n 天数: {record.get('days', 0)}"
message += f"\n 时间: {activation_time}\n"
if failed_codes:
message += f"\n\n以下激活码验证失败:\n" + "\n".join(failed_codes)
return True, message, member_info
except Exception as e:
logging.error(f"处理激活结果时出错: {str(e)}")
return False, f"处理激活结果失败: {str(e)}", None
except Exception as e:
logging.error(f"激活码验证过程出错: {str(e)}")
return False, f"激活失败: {str(e)}", None
def reset_machine_id(self) -> bool:
"""重置机器码"""
try:
# 读取package.json
with open(self.package_json, "r", encoding="utf-8") as f:
data = json.load(f)
# 删除machineId
if "machineId" in data:
del data["machineId"]
# 保存文件
with open(self.package_json, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
logging.info("机器码重置完成")
return True
except Exception as e:
logging.error(f"重置机器码失败: {str(e)}")
return False
def activate_and_switch(self, activation_code: str) -> Tuple[bool, str]:
"""激活并切换账号
Returns:
Tuple[bool, str]: (是否成功, 提示消息)
"""
try:
# 验证激活码
success, message, account_info = self.check_activation_code(activation_code)
return success, message
except Exception as e:
logging.error(f"激活过程出错: {str(e)}")
return False, f"激活失败: {str(e)}"
def get_member_status(self) -> Optional[Dict]:
"""获取会员状态
Returns:
Optional[Dict]: 会员状态信息
"""
try:
# 读取保存的会员信息
member_info = self.config.load_member_info()
# 构造状态检查请求
endpoint = "https://cursorapi.nosqli.com/admin/api.member/status"
data = {
"machine_id": self.hardware_id
}
headers = {
"Content-Type": "application/json"
}
try:
response = requests.post(endpoint, json=data, headers=headers, timeout=10)
response_data = response.json()
if response_data.get("code") == 200:
# 正常状态
data = response_data.get("data", {})
status = data.get("status", "inactive")
# 构造会员信息
member_info = {
"hardware_id": data.get("machine_id", self.hardware_id),
"expire_time": data.get("expire_time", ""),
"days_left": data.get("days_left", 0), # 使用days_left
"total_days": data.get("total_days", 0), # 使用total_days
"status": status,
"activation_records": data.get("activation_records", []) # 保存激活记录
}
# 打印调试信息
logging.info(f"API返回数据: {data}")
logging.info(f"处理后的会员信息: {member_info}")
self.config.save_member_info(member_info)
return member_info
elif response_data.get("code") == 401:
# 未激活或已过期
logging.warning("会员未激活或已过期")
return self._get_inactive_status()
elif response_data.get("code") == 400:
# 参数错误
error_msg = response_data.get("msg", "参数错误")
logging.error(f"获取会员状态失败: {error_msg}")
return self._get_inactive_status()
elif response_data.get("code") == 500:
# 系统错误
error_msg = response_data.get("msg", "系统错误")
logging.error(f"获取会员状态失败: {error_msg}")
return self._get_inactive_status()
else:
# 其他未知错误
error_msg = response_data.get("msg", "未知错误")
logging.error(f"获取会员状态失败: {error_msg}")
return self._get_inactive_status()
except requests.RequestException as e:
logging.error(f"请求会员状态失败: {str(e)}")
return self._get_inactive_status()
except Exception as e:
logging.error(f"获取会员状态出错: {str(e)}")
return self._get_inactive_status()
def _get_inactive_status(self) -> Dict:
"""获取未激活状态的默认信息"""
return {
"hardware_id": self.hardware_id,
"expire_time": "",
"days": 0,
"total_days": 0,
"status": "inactive",
"last_activation": {},
"activation_records": []
}
def restart_cursor(self) -> bool:
"""重启Cursor编辑器
Returns:
bool: 是否成功重启
"""
try:
logging.info("正在重启Cursor...")
if sys.platform == "win32":
# Windows系统
# 关闭Cursor
os.system("taskkill /f /im Cursor.exe 2>nul")
time.sleep(2)
# 获取Cursor安装路径
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
# 启动Cursor
os.startfile(str(cursor_exe))
logging.info("Cursor重启成功")
return True
else:
logging.error(f"未找到Cursor程序: {cursor_exe}")
return False
elif sys.platform == "darwin":
# macOS系统
os.system("killall Cursor 2>/dev/null")
time.sleep(2)
os.system("open -a Cursor")
logging.info("Cursor重启成功")
return True
elif sys.platform == "linux":
# Linux系统
os.system("pkill -f cursor")
time.sleep(2)
os.system("cursor &")
logging.info("Cursor重启成功")
return True
else:
logging.error(f"不支持的操作系统: {sys.platform}")
return False
except Exception as e:
logging.error(f"重启Cursor时发生错误: {str(e)}")
return False
def refresh_cursor_auth(self) -> Tuple[bool, str]:
"""刷新Cursor授权
Returns:
Tuple[bool, str]: (是否成功, 提示消息)
"""
try:
# 获取未使用的账号
endpoint = "https://cursorapi.nosqli.com/admin/api.account/getUnused"
data = {
"machine_id": self.hardware_id
}
headers = {
"Content-Type": "application/json"
}
try:
# 添加SSL验证选项和超时设置
response = requests.post(
endpoint,
json=data,
headers=headers,
timeout=30, # 增加超时时间
verify=False, # 禁用SSL验证
)
# 禁用SSL警告
requests.packages.urllib3.disable_warnings()
response_data = response.json()
if response_data.get("code") == 200:
account_data = response_data.get("data", {})
# 获取账号信息
email = account_data.get("email", "")
access_token = account_data.get("access_token", "")
refresh_token = account_data.get("refresh_token", "")
expire_time = account_data.get("expire_time", "")
days_left = account_data.get("days_left", 0)
if not all([email, access_token, refresh_token]):
return False, "获取账号信息不完整"
# 更新Cursor认证信息
if not self.auth_manager.update_auth(email, access_token, refresh_token):
return False, "更新Cursor认证信息失败"
# 重置机器码
if not self.reset_machine_id():
return False, "重置机器码失败"
# 刷新注册表
if not self.registry.refresh_registry():
logging.warning("注册表刷新失败,但不影响主要功能")
# 重启Cursor
if not self.auth_manager.restart_cursor():
return False, "重启Cursor失败"
# 重启Cursor
# if not self.restart_cursor():
# logging.warning("Cursor重启失败请手动重启")
# return True, f"授权刷新成功请手动重启Cursor编辑器\n邮箱: {email}\n到期时间: {expire_time}\n剩余天数: {days_left}天"
return True, f"授权刷新成功Cursor编辑器已重启\n邮箱: {email}\n"
elif response_data.get("code") == 404:
return False, "没有可用的未使用账号"
else:
error_msg = response_data.get("msg", "未知错误")
logging.error(f"获取未使用账号失败: {error_msg}")
return False, f"获取账号失败: {error_msg}"
except requests.exceptions.SSLError as e:
logging.error(f"SSL验证失败: {str(e)}")
return False, "SSL验证失败,请检查网络设置"
except requests.exceptions.ConnectionError as e:
logging.error(f"网络连接错误: {str(e)}")
return False, "网络连接失败,请检查网络设置"
except requests.exceptions.Timeout as e:
logging.error(f"请求超时: {str(e)}")
return False, "请求超时,请稍后重试"
except requests.RequestException as e:
logging.error(f"请求失败: {str(e)}")
return False, f"网络请求失败: {str(e)}"
except Exception as e:
logging.error(f"未知错误: {str(e)}")
return False, f"发生未知错误: {str(e)}"
except Exception as e:
logging.error(f"刷新授权过程出错: {str(e)}")
return False, f"刷新失败: {str(e)}"
def main():
"""主函数"""
try:
switcher = AccountSwitcher()
print("\n=== Cursor账号切换工具 ===")
print("1. 激活并切换账号")
print("2. 仅重置机器码")
while True:
try:
choice = int(input("\n请选择操作 (1 或 2): ").strip())
if choice in [1, 2]:
break
else:
print("无效的选项,请重新输入")
except ValueError:
print("请输入有效的数字")
if choice == 1:
activation_code = input("请输入激活码: ").strip()
if switcher.activate_and_switch(activation_code):
print("\n账号激活成功!")
else:
print("\n账号激活失败,请查看日志了解详细信息")
else:
if switcher.reset_machine_id():
print("\n机器码重置成功!")
else:
print("\n机器码重置失败,请查看日志了解详细信息")
except Exception as e:
logging.error(f"程序执行出错: {str(e)}")
print("\n程序执行出错,请查看日志了解详细信息")
finally:
input("\n按回车键退出...")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,134 @@
import os
import json
import sys
import time
import logging
import sqlite3
from pathlib import Path
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}")
self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
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:
logging.warning("没有提供任何要更新的值")
return False
conn = None
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
for key, value in updates:
# 检查key是否存在
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:
logging.info(f"成功更新 {key.split('/')[-1]}")
else:
logging.warning(f"未找到 {key.split('/')[-1]} 或值未变化")
conn.commit()
logging.info(f"认证信息更新成功: {email}")
return True
except sqlite3.Error as e:
logging.error(f"数据库错误: {str(e)}")
return False
except Exception as e:
logging.error(f"更新认证信息失败: {str(e)}")
return False
finally:
if conn:
conn.close()
def restart_cursor(self) -> bool:
"""重启Cursor编辑器
Returns:
bool: 是否成功重启
"""
try:
logging.info("正在重启Cursor...")
if sys.platform == "win32":
# Windows系统
# 关闭Cursor
os.system("taskkill /f /im Cursor.exe 2>nul")
time.sleep(2)
# 获取Cursor安装路径
cursor_exe = self.cursor_path / "Cursor.exe"
if cursor_exe.exists():
# 启动Cursor
os.startfile(str(cursor_exe))
logging.info("Cursor重启成功")
return True
else:
logging.error(f"未找到Cursor程序: {cursor_exe}")
return False
elif sys.platform == "darwin":
# macOS系统
os.system("killall Cursor 2>/dev/null")
time.sleep(2)
os.system("open -a Cursor")
logging.info("Cursor重启成功")
return True
elif sys.platform == "linux":
# Linux系统
os.system("pkill -f cursor")
time.sleep(2)
os.system("cursor &")
logging.info("Cursor重启成功")
return True
else:
logging.error(f"不支持的操作系统: {sys.platform}")
return False
except Exception as e:
logging.error(f"重启Cursor时发生错误: {str(e)}")
return False

View File

@@ -0,0 +1,295 @@
import sys
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path
import logging
import os
from PIL import Image, ImageTk
sys.path.append(str(Path(__file__).parent.parent))
from utils.config import Config
from account_switcher import AccountSwitcher
class MainWindow:
def __init__(self):
self.config = Config()
self.switcher = AccountSwitcher()
self.root = tk.Tk()
self.root.title("听泉Cursor助手 v2.2.2 (本机Cursor版本: 0.45.11)")
self.root.geometry("600x500") # 调整窗口大小
self.root.resizable(True, True) # 允许调整窗口大小
# 设置窗口最小尺寸
self.root.minsize(600, 500)
try:
# 设置图标 - 使用PIL
current_dir = os.path.dirname(os.path.abspath(__file__))
icon_path = os.path.join(os.path.dirname(current_dir), "icon", "th.jpg")
if os.path.exists(icon_path):
# 打开并调整图片大小
img = Image.open(icon_path)
img = img.resize((32, 32), Image.Resampling.LANCZOS)
# 转换为PhotoImage
self.icon = ImageTk.PhotoImage(img)
self.root.iconphoto(True, self.icon)
logging.info(f"成功加载图标: {icon_path}")
else:
logging.error(f"图标文件不存在: {icon_path}")
except Exception as e:
logging.error(f"设置图标失败: {str(e)}")
# 设置关闭窗口处理
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
# 初始化状态变量
self.status_var = tk.StringVar(value="")
# 设置样式
self.style = ttk.Style()
self.style.configure("TButton", padding=5, font=("Microsoft YaHei UI", 9))
self.style.configure("TLabelframe", padding=10, font=("Microsoft YaHei UI", 9))
self.style.configure("TLabel", padding=2, font=("Microsoft YaHei UI", 9))
self.style.configure("Custom.TButton", padding=10, font=("Microsoft YaHei UI", 9))
self.style.configure("Action.TButton", padding=8, font=("Microsoft YaHei UI", 9))
self.setup_ui()
# 启动时检查一次状态
self.check_status()
def setup_ui(self):
"""设置UI界面"""
# 主框架
main_frame = ttk.Frame(self.root, padding=10)
main_frame.pack(fill="both", expand=True)
# 功能菜单
menu_frame = ttk.Frame(main_frame)
menu_frame.pack(fill="x", pady=(0, 10))
ttk.Label(menu_frame, text="功能(F)").pack(side="left")
# 设备ID区域
device_frame = ttk.Frame(main_frame)
device_frame.pack(fill="x", pady=(0, 10))
ttk.Label(device_frame, text="设备识别码(勿动):").pack(side="left")
self.hardware_id_var = tk.StringVar(value=self.switcher.hardware_id)
device_id_entry = ttk.Entry(device_frame, textvariable=self.hardware_id_var, width=35, state="readonly")
device_id_entry.pack(side="left", padx=5)
copy_btn = ttk.Button(device_frame, text="复制ID", command=self.copy_device_id, width=8)
copy_btn.pack(side="left")
# 会员状态区域
status_frame = ttk.LabelFrame(main_frame, text="会员状态")
status_frame.pack(fill="x", pady=(0, 10))
self.status_text = tk.Text(status_frame, height=5, width=40, font=("Microsoft YaHei UI", 9))
self.status_text.pack(fill="both", padx=5, pady=5)
self.status_text.config(state="disabled")
# 激活区域
activation_frame = ttk.LabelFrame(main_frame, text="激活(叠加)会员,多个激活码可叠加整体时长")
activation_frame.pack(fill="x", pady=(0, 10))
input_frame = ttk.Frame(activation_frame)
input_frame.pack(fill="x", padx=5, pady=5)
ttk.Label(input_frame, text="激活码:").pack(side="left")
self.activation_var = tk.StringVar()
activation_entry = ttk.Entry(input_frame, textvariable=self.activation_var, width=35)
activation_entry.pack(side="left", padx=5)
activate_btn = ttk.Button(input_frame, text="激活", command=self.activate_account, width=8)
activate_btn.pack(side="left")
# 操作按钮区域
btn_frame = ttk.Frame(main_frame)
btn_frame.pack(fill="x", pady=5)
self.style.configure("Action.TButton", padding=8)
ttk.Button(btn_frame, text="刷新Cursor编辑器授权", command=self.reset_machine_id, style="Action.TButton").pack(fill="x", pady=2)
ttk.Button(btn_frame, text="突破Cursor0.45.x限制", command=self.dummy_function, style="Action.TButton").pack(fill="x", pady=2)
ttk.Button(btn_frame, text="禁用Cursor版本更新", command=self.dummy_function, style="Action.TButton").pack(fill="x", pady=2)
def copy_device_id(self):
"""复制设备ID到剪贴板"""
# 先检查状态
if not self.check_status():
return
self.root.clipboard_clear()
self.root.clipboard_append(self.hardware_id_var.get())
def activate_account(self):
"""激活账号"""
code = self.activation_var.get().strip()
if not code:
messagebox.showwarning("提示", "请输入激活码")
return
self.status_var.set("正在激活...")
self.root.update()
try:
success, message, account_info = self.switcher.check_activation_code(code)
if success:
# 更新会员信息显示
self.update_status_display(account_info)
messagebox.showinfo("激活成功", "激活成功!\n" + message)
self.status_var.set("激活成功")
# 清空激活码输入框
self.activation_var.set("")
else:
messagebox.showerror("激活失败", message)
self.status_var.set("激活失败")
# 激活后检查一次状态
self.check_status()
except Exception as e:
messagebox.showerror("错误", f"激活失败: {str(e)}")
self.status_var.set("发生错误")
# 出错后也检查状态
self.check_status()
def update_status_display(self, status_info: dict):
"""更新状态显示"""
# 打印API返回的原始数据
logging.info("=== API返回数据 ===")
logging.info(f"状态信息: {status_info}")
if 'activation_records' in status_info:
logging.info("激活记录:")
for record in status_info['activation_records']:
logging.info(f"- 记录: {record}")
# 启用文本框编辑
self.status_text.config(state="normal")
# 清空当前内容
self.status_text.delete(1.0, tk.END)
# 更新状态文本
status_map = {
"active": "正常",
"inactive": "未激活",
"expired": "已过期"
}
status_text = status_map.get(status_info.get('status', 'inactive'), "未知")
# 构建状态文本
status_lines = [
f"会员状态:{status_text}",
f"到期时间:{status_info.get('expire_time', '')}",
f"总天数:{status_info.get('total_days', 0)}",
f"剩余天数:{status_info.get('days_left', 0)}"
]
# 如果有激活记录,显示最近一次激活信息
activation_records = status_info.get('activation_records', [])
if activation_records:
latest_record = activation_records[-1] # 获取最新的激活记录
device_info = latest_record.get('device_info', {})
status_lines.extend([
"",
"最近激活信息:",
f"激活码:{latest_record.get('code', '')}",
f"激活时间:{latest_record.get('activation_time', '')}",
f"增加天数:{latest_record.get('days', 0)}",
"",
"设备信息:",
f"系统:{device_info.get('os', '')}",
f"设备名:{device_info.get('device_name', '')}",
f"IP地址{device_info.get('ip', '')}",
f"地区:{device_info.get('location', '--')}"
])
# 写入状态信息
self.status_text.insert(tk.END, "\n".join(status_lines))
# 禁用文本框编辑
self.status_text.config(state="disabled")
def check_status(self):
"""检查会员状态
Returns:
bool: True 表示激活状态正常False 表示未激活或已过期
"""
try:
self.status_var.set("正在检查状态...")
self.root.update()
status = self.switcher.get_member_status()
if status:
self.update_status_display(status)
if status.get('status') == 'inactive':
messagebox.showwarning("警告", "当前设备未激活或激活已失效")
self.status_var.set("未激活")
return False
self.status_var.set("状态检查完成")
return True
else:
# 更新为未激活状态
inactive_status = {
"hardware_id": self.switcher.hardware_id,
"expire_time": "",
"days": 0,
"total_days": 0,
"status": "inactive",
"last_activation": {}
}
self.update_status_display(inactive_status)
self.status_var.set("未激活")
messagebox.showwarning("警告", "当前设备未激活")
return False
except Exception as e:
logging.error(f"检查状态失败: {str(e)}")
self.status_var.set("状态检查失败")
# 显示错误消息
messagebox.showerror("错误", f"检查状态失败: {str(e)}")
return False
def minimize_window(self):
"""最小化窗口"""
self.root.iconify()
def maximize_window(self):
"""最大化/还原窗口"""
if self.root.state() == 'zoomed':
self.root.state('normal')
else:
self.root.state('zoomed')
def on_closing(self):
"""窗口关闭处理"""
try:
logging.info("正在关闭程序...")
self.root.quit()
except Exception as e:
logging.error(f"关闭程序时出错: {str(e)}")
finally:
self.root.destroy()
def run(self):
"""运行程序"""
self.root.mainloop()
def dummy_function(self):
"""占位函数"""
# 先检查状态
if not self.check_status():
return
messagebox.showinfo("提示", "此功能暂未实现")
def reset_machine_id(self):
"""刷新Cursor编辑器授权"""
try:
# 刷新授权
success, message = self.switcher.refresh_cursor_auth()
if success:
messagebox.showinfo("成功", "Cursor编辑器授权刷新成功\n" + message)
else:
messagebox.showerror("错误", message)
except Exception as e:
messagebox.showerror("错误", f"刷新失败: {str(e)}")

BIN
interactive/icon/th (1).jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
interactive/icon/th (2).jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
interactive/icon/th.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
interactive/icon/two.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

61
interactive/main.py Normal file
View File

@@ -0,0 +1,61 @@
import logging
import sys
import traceback
from pathlib import Path
from gui.main_window import MainWindow
def setup_logging():
"""设置日志"""
try:
log_dir = Path.home() / ".cursor_switcher" / "logs"
log_dir.mkdir(parents=True, exist_ok=True)
log_file = log_dir / "switcher.log"
# 同时输出到文件和控制台
handlers = [logging.FileHandler(log_file, encoding="utf-8")]
if sys.stdout is not None:
handlers.append(logging.StreamHandler(sys.stdout))
logging.basicConfig(
level=logging.INFO, # 使用INFO级别以显示更多信息
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=handlers
)
logging.info("日志系统初始化成功")
except Exception as e:
print(f"设置日志系统失败: {str(e)}")
def main():
"""主函数"""
try:
print("正在启动程序...")
setup_logging()
# 检查Python版本
logging.info(f"Python版本: {sys.version}")
# 检查工作目录
logging.info(f"当前工作目录: {Path.cwd()}")
# 检查模块路径
logging.info("Python路径:")
for p in sys.path:
logging.info(f" - {p}")
logging.info("正在初始化主窗口...")
window = MainWindow()
logging.info("正在启动主窗口...")
window.run()
except Exception as e:
error_msg = f"程序运行出错: {str(e)}\n{traceback.format_exc()}"
logging.error(error_msg)
print(error_msg) # 直接打印错误信息
# 保持窗口不关闭
input("按回车键退出...")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,4 @@
requests==2.31.0
pyinstaller==6.3.0
pillow==10.2.0 # 用于处理图标
setuptools==65.5.1 # 解决pkg_resources.extern问题

View File

@@ -0,0 +1,67 @@
import os
import json
import logging
from pathlib import Path
class Config:
def __init__(self):
self.api_base_url = "https://cursorapi.nosqli.com/admin"
self.config_dir = Path(os.path.expanduser("~")) / ".cursor_switcher"
self.config_file = self.config_dir / "config.json"
self.member_file = self.config_dir / "member.json"
self.load_config()
def load_config(self):
"""加载配置"""
try:
self.config_dir.mkdir(parents=True, exist_ok=True)
if not self.config_file.exists():
self.save_default_config()
with open(self.config_file, "r", encoding="utf-8") as f:
config = json.load(f)
self.api_token = config.get("api_token", "")
except Exception as e:
logging.error(f"加载配置失败: {str(e)}")
self.api_token = ""
def save_member_info(self, info: dict):
"""保存会员信息"""
try:
with open(self.member_file, "w", encoding="utf-8") as f:
json.dump(info, f, indent=2, ensure_ascii=False)
except Exception as e:
logging.error(f"保存会员信息失败: {str(e)}")
def load_member_info(self) -> dict:
"""读取会员信息"""
try:
if self.member_file.exists():
with open(self.member_file, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
logging.error(f"读取会员信息失败: {str(e)}")
return {
"expire_time": "",
"days": 0,
"new_days": 0
}
def save_default_config(self):
"""保存默认配置"""
config = {
"api_token": ""
}
with open(self.config_file, "w", encoding="utf-8") as f:
json.dump(config, f, indent=2, ensure_ascii=False)
def save_config(self, api_token: str):
"""保存新的配置"""
config = {
"api_token": api_token
}
with open(self.config_file, "w", encoding="utf-8") as f:
json.dump(config, f, indent=2, ensure_ascii=False)
self.api_token = api_token

View File

@@ -0,0 +1,72 @@
import os
import winreg
import logging
from pathlib import Path
class CursorRegistry:
"""Cursor注册表操作工具类"""
def __init__(self):
self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor"
self.app_path = self.cursor_path / "resources" / "app"
def refresh_registry(self) -> bool:
"""刷新Cursor相关的注册表项
Returns:
bool: 是否成功
"""
try:
# 获取Cursor安装路径
cursor_exe = self.cursor_path / "Cursor.exe"
if not cursor_exe.exists():
logging.error("未找到Cursor.exe")
return False
# 刷新注册表项
try:
# 打开HKEY_CURRENT_USER\Software\Classes\Directory\Background\shell\Cursor
key_path = r"Software\Classes\Directory\Background\shell\Cursor"
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_WRITE) as key:
winreg.SetValueEx(key, "Icon", 0, winreg.REG_SZ, str(cursor_exe))
# 打开command子键
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path + r"\command", 0, winreg.KEY_WRITE) as key:
winreg.SetValueEx(key, "", 0, winreg.REG_SZ, f'"{str(cursor_exe)}" "%V"')
logging.info("注册表刷新成功")
return True
except WindowsError as e:
logging.error(f"刷新注册表失败: {str(e)}")
return False
except Exception as e:
logging.error(f"刷新注册表过程出错: {str(e)}")
return False
def clean_registry(self) -> bool:
"""清理Cursor相关的注册表项
Returns:
bool: 是否成功
"""
try:
# 删除注册表项
try:
key_path = r"Software\Classes\Directory\Background\shell\Cursor"
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, key_path + r"\command")
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, key_path)
logging.info("注册表清理成功")
return True
except WindowsError as e:
if e.winerror == 2: # 找不到注册表项
logging.info("注册表项不存在,无需清理")
return True
logging.error(f"清理注册表失败: {str(e)}")
return False
except Exception as e:
logging.error(f"清理注册表过程出错: {str(e)}")
return False

View File

@@ -3,4 +3,5 @@ colorama==0.4.6
python-dotenv==1.0.0 python-dotenv==1.0.0
pyinstaller pyinstaller
playwright==1.41.1 playwright==1.41.1
requests==2.31.0 requests>=2.31.0
pytz>=2024.1

85
test_api_response.py Normal file
View File

@@ -0,0 +1,85 @@
import requests
import json
from datetime import datetime
def test_member_status(machine_id: str):
"""测试会员状态接口"""
print(f"\n=== 测试会员状态 ===")
print(f"设备码: {machine_id}")
try:
# 构造状态检查请求
endpoint = "https://cursorapi.nosqli.com/admin/api.member/status"
data = {
"machine_id": machine_id
}
headers = {
"Content-Type": "application/json"
}
print("\n发送请求...")
response = requests.post(endpoint, json=data, headers=headers, timeout=10, verify=False)
print(f"状态码: {response.status_code}")
print("\n响应数据:")
response_data = response.json()
print(json.dumps(response_data, indent=2, ensure_ascii=False))
if response_data.get("code") == 200:
data = response_data.get("data", {})
print("\n解析后的信息:")
print(f"状态: {data.get('status', 'unknown')}")
print(f"到期时间: {data.get('expire_time', '')}")
print(f"剩余天数: {data.get('days_left', 0)}")
print(f"总天数: {data.get('total_days', 0)}")
print("\n激活记录:")
for record in data.get("activation_records", []):
print(f"- 激活码: {record.get('code', '')}")
print(f" 激活时间: {record.get('activation_time', '')}")
print(f" 天数: {record.get('days', 0)}")
print()
except Exception as e:
print(f"\n请求失败: {str(e)}")
def test_get_unused_account(machine_id: str):
"""测试获取未使用账号接口"""
print(f"\n=== 测试获取未使用账号 ===")
print(f"设备码: {machine_id}")
try:
# 构造请求
endpoint = "https://cursorapi.nosqli.com/admin/api.account/getUnused"
data = {
"machine_id": machine_id
}
headers = {
"Content-Type": "application/json"
}
print("\n发送请求...")
response = requests.post(endpoint, json=data, headers=headers, timeout=30, verify=False)
print(f"状态码: {response.status_code}")
print("\n响应数据:")
response_data = response.json()
print(json.dumps(response_data, indent=2, ensure_ascii=False))
if response_data.get("code") == 200:
account_data = response_data.get("data", {})
print("\n账号信息:")
print(f"邮箱: {account_data.get('email', '')}")
print(f"到期时间: {account_data.get('expire_time', '')}")
print(f"剩余天数: {account_data.get('days_left', 0)}")
except Exception as e:
print(f"\n请求失败: {str(e)}")
if __name__ == "__main__":
# 禁用SSL警告
requests.packages.urllib3.disable_warnings()
machine_id = "b2c32bdeff8d3f0d33dc88f2aadea6bc"
test_member_status(machine_id)
test_get_unused_account(machine_id)

38
test_hardware_id.py Normal file
View File

@@ -0,0 +1,38 @@
import subprocess
import hashlib
import logging
def get_hardware_id() -> str:
"""获取硬件唯一标识"""
try:
print("正在获取CPU信息...")
cpu_info = subprocess.check_output('wmic cpu get ProcessorId').decode()
cpu_id = cpu_info.split('\n')[1].strip()
print(f"CPU ID: {cpu_id}")
print("\n正在获取主板序列号...")
board_info = subprocess.check_output('wmic baseboard get SerialNumber').decode()
board_id = board_info.split('\n')[1].strip()
print(f"主板序列号: {board_id}")
print("\n正在获取BIOS序列号...")
bios_info = subprocess.check_output('wmic bios get SerialNumber').decode()
bios_id = bios_info.split('\n')[1].strip()
print(f"BIOS序列号: {bios_id}")
# 组合信息并生成哈希
combined = f"{cpu_id}:{board_id}:{bios_id}"
print(f"\n组合信息: {combined}")
hardware_id = hashlib.md5(combined.encode()).hexdigest()
print(f"\n生成的硬件ID: {hardware_id}")
return hardware_id
except Exception as e:
print(f"获取硬件ID失败: {str(e)}")
return ""
if __name__ == "__main__":
print("=== 硬件ID测试 ===\n")
hardware_id = get_hardware_id()
if hardware_id:
print("\n测试完成!")