Skip to content

每日获取 Cursor Token (内联脚本) #3

每日获取 Cursor Token (内联脚本)

每日获取 Cursor Token (内联脚本) #3

Workflow file for this run

name: 每日获取 Cursor Token (内联脚本)
on:
schedule:
- cron: '0 22 * * *' # 每天 UTC 时间 22:00 执行 (北京时间早上 6:00)
workflow_dispatch:
jobs:
fetch-token:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 设置 Python 环境
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: 安装依赖
run: pip install requests psutil
- name: 恢复缓存
id: cache-tokens
uses: actions/cache@v4
with:
path: token_cache.json
key: ${{ runner.os }}-tokens-${{ github.sha }} # 使用 commit SHA 作为缓存键
restore-keys: |
${{ runner.os }}-tokens-
- name: 运行 Token 获取脚本
env:
ACCESS_CODE: ${{ secrets.ACCESS_CODE }}
run: |
python -c '
import json
import logging
import os
import platform
import time
import requests
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Optional, Tuple
# 配置日志记录
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
# 常量配置类 (与之前相同)
class Config:
API_URL = "https://cursor.ccopilot.org/api/get_next_token.php"
PROCESS_TIMEOUT = 5
CURSOR_PROCESS_NAMES = ["cursor.exe", "cursor"]
DB_KEYS = {
"email": "cursorAuth/cachedEmail",
"access_token": "cursorAuth/accessToken",
"refresh_token": "cursorAuth/refreshToken",
}
MIN_PATCH_VERSION = "0.45.0"
VERSION_PATTERN = r"^\d+\.\d+\.\d+$"
@dataclass
class TokenMetadata:
"""Token 元数据(不包含 token 本身)"""
mac_machine_id: str
machine_id: str
dev_device_id: str
email: str
fetch_time: str # 添加获取时间
@classmethod
def from_dict(cls, data: Dict[str, str]) -> "TokenMetadata":
"""从字典创建 TokenMetadata 实例"""
return cls(
mac_machine_id=data["mac_machine_id"],
machine_id=data["machine_id"],
dev_device_id=data["dev_device_id"],
email=data["email"],
fetch_time=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), # 获取当前时间
)
class TokenManager:
"""Token 管理(修改为返回包含 token 的完整响应)"""
@staticmethod
def fetch_token_data(access_code: str) -> Optional[Dict]:
"""获取Token数据,直接返回原始响应"""
logger.info(f"正在使用授权码 {access_code} 获取 Token 数据...")
try:
response = requests.get(f"{Config.API_URL}?accessCode={access_code}")
response.raise_for_status() # 检查请求是否成功
data = response.json() # 假设响应是 JSON 格式
if data.get("code") == 0:
logger.info("成功获取 Token 数据")
else:
logger.warning(f"获取 Token 失败: {data.get('message', '未知错误')}") # 修正: 使用单引号
return data # 直接返回
except requests.RequestException as e:
logger.error(f"获取 Token 数据时发生网络错误: {e}")
return None
except json.JSONDecodeError:
logger.error("响应内容不是有效的 JSON 格式")
return None
except Exception as e:
logger.error(f"获取 Token 数据时发生未知错误: {e}")
return None
class FilePathManager:
"""文件路径管理器(本脚本中仅用于确定输出路径,简化)"""
@staticmethod
def get_output_dir() -> Path:
"""获取输出目录"""
return Path.cwd() # 当前工作目录
@staticmethod
def get_storage_path() -> Path:
"""获取storage.json文件路径"""
system = platform.system()
if system == "Windows":
return Path(os.getenv("APPDATA")) / "Cursor" / "User" / "globalStorage" / "storage.json"
elif system == "Darwin":
return Path.home() / "Library" / "Application Support" / "Cursor" / "User" / "globalStorage" / "storage.json"
elif system == "Linux":
return Path.home() / ".config" / "Cursor" / "User" / "globalStorage" / "storage.json"
raise OSError(f"不支持的操作系统: {system}")
@staticmethod
def get_db_path() -> Path:
"""获取数据库文件路径"""
system = platform.system()
if system == "Windows":
return Path(os.getenv("APPDATA")) / "Cursor" / "User" / "globalStorage" / "state.vscdb"
elif system == "Darwin":
return Path.home() / "Library" / "Application Support" / "Cursor" / "User" / "globalStorage" / "state.vscdb"
elif system == "Linux":
return Path.home() / ".config" / "Cursor" / "User" / "globalStorage" / "state.vscdb"
raise OSError(f"不支持的操作系统: {system}")
@staticmethod
def get_cursor_app_paths() -> Tuple[Path, Path]:
"""获取Cursor应用相关路径"""
system = platform.system()
if system == "Windows":
base_path = Path(os.getenv("LOCALAPPDATA", "")) / "Programs" / "Cursor" / "resources" / "app"
elif system == "Darwin":
base_path = Path("/Applications/Cursor.app/Contents/Resources/app")
elif system == "Linux":
# 检查可能的Linux安装路径
possible_paths = [
Path("/opt/Cursor/resources/app"),
Path("/usr/share/cursor/resources/app"),
]
base_path = next((p for p in possible_paths if p.exists()), None)
if not base_path:
raise OSError("在Linux系统上未找到Cursor安装路径")
else:
raise OSError(f"不支持的操作系统: {system}")
return base_path / "package.json", base_path / "out" / "main.js"
def main() -> None:
"""主程序(GitHub Actions 版本,使用缓存)"""
access_code = os.getenv("ACCESS_CODE")
if not access_code:
logger.error("未设置 ACCESS_CODE 环境变量,程序退出。")
return
num_attempts = 5
delay_between_attempts = 2.0
output_dir = FilePathManager.get_output_dir()
output_file = output_dir / "token_metadata.txt" # 存储元数据的文件
cache_file = output_dir / "token_cache.json" # 缓存文件
# 尝试从缓存文件加载现有 token 数据
cached_tokens = []
if cache_file.exists():
try:
with open(cache_file, "r", encoding="utf-8") as f:
cached_tokens = json.load(f)
if not isinstance(cached_tokens, list):
cached_tokens = [cached_tokens]
except json.JSONDecodeError:
logger.warning(f"缓存文件 {cache_file} 内容不是有效的 JSON,将忽略。")
cached_tokens = []
# 尝试读取现有元数据文件内容 (如果文件存在)
existing_metadata = []
if output_file.exists():
try:
with open(output_file, "r", encoding="utf-8") as f:
existing_metadata = json.load(f)
if not isinstance(existing_metadata, list):
existing_metadata = [existing_metadata]
except json.JSONDecodeError:
logger.warning(f"文件 {output_file} 内容不是有效的 JSON,将覆盖。")
existing_metadata = []
new_tokens = [] # 用于存储本次获取的新 token
for attempt in range(1, num_attempts + 1):
logger.info(f"尝试第 {attempt} 次获取...")
if token_data := TokenManager.fetch_token_data(access_code):
try:
# 提取元数据
metadata = TokenMetadata.from_dict(token_data)
existing_metadata.append(metadata.__dict__) # 将元数据对象转换为字典并存储
# 将完整的 token 数据添加到 new_tokens 列表
new_tokens.append(token_data)
logger.info(f"第 {attempt} 次获取成功.")
except Exception as e:
logger.error(f"处理第 {attempt} 次获取的数据时出错: {e}")
else:
logger.error(f"第 {attempt} 次获取失败。")
if attempt < num_attempts:
logger.info(f"等待 {delay_between_attempts} 秒...")
time.sleep(delay_between_attempts)
# 合并新旧 token 数据, 并去重
all_tokens = cached_tokens + new_tokens
unique_tokens = []
seen_tokens = set() # 使用集合来跟踪已经见过的 token
for token in all_tokens:
token_value = token.get("token")
if token_value and token_value not in seen_tokens:
unique_tokens.append(token)
seen_tokens.add(token_value)
# 将元数据写入文件
try:
with open(output_file, "w", encoding="utf-8") as f:
json.dump(existing_metadata, f, indent=4, ensure_ascii=False)
logger.info(f"Token 元数据已追加到: {output_file}")
except Exception as e:
logger.error(f"写入元数据文件时出错: {e}")
# 将更新后的 token 数据写入缓存文件
try:
with open(cache_file, "w", encoding="utf-8") as f:
json.dump(unique_tokens, f, indent=4, ensure_ascii=False)
logger.info(f"Token 数据已更新到缓存文件: {cache_file}")
except Exception as e:
logger.error(f"写入缓存文件时出错: {e}")
# 运行主函数
if __name__ == "__main__":
main()
'
- name: 提交元数据更改
run: |
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
git add token_metadata.txt
git commit -m "自动更新 Token 元数据" || echo "没有新的 Token 元数据"
git push
- name: 保存缓存
if: steps.cache-tokens.outputs.cache-hit != 'true'
run: echo "缓存已更新"