mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2025-12-26 13:40:39 +03:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fecdcb933e | ||
|
|
98adbb76b7 | ||
|
|
0688cdbfcd | ||
|
|
2bf2cf6ab2 | ||
|
|
0f4cf16d90 | ||
|
|
3edef109d4 | ||
|
|
f358ee9aec | ||
|
|
ca93d966cf | ||
|
|
abc354f149 | ||
|
|
9f002970d8 | ||
|
|
803fea0b71 | ||
|
|
ddaafd5c3d | ||
|
|
cdb9c73755 | ||
|
|
08c4b0873e | ||
|
|
301a4aa88d | ||
|
|
34e9d536c1 | ||
|
|
19fe4c85f8 |
58
README.md
58
README.md
@@ -12,36 +12,52 @@
|
||||
|
||||
</p>
|
||||
|
||||
This is a tool to automatically bypass Cursor's membership check, automatically upgrade to "pro" membership, support Windows and macOS systems, send Token requests in real-time, and reset Cursor's configuration.
|
||||
This is a tool to automatically register (except for Google verification code), support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
|
||||
|
||||
這是一個自動化工具,自動繞過Cursor的會員檢查,自動升級為 "pro" 會員,支持 Windows 和 macOS 系統,實時發送Token請求,重置Cursor的配置。
|
||||
這是一個自動化工具,自動註冊(除了Google驗證碼),支持 Windows 和 macOS 系統,完成Auth驗證,重置Cursor的配置。
|
||||
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-11_00-51-07.png" alt="win" width="400"/><br>
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-13_13-49-55.png" alt="mac" width="400"/><br>
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-11_22-33-09.gif" alt="Cursor Pro Logo" width="600"/><br>
|
||||
<img src="./images/pro_2025-01-14_14-40-37.png" alt="new" width="400"/><br>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<p align="center">
|
||||
<img src="./images/free_2025-01-14_14-59-15.png" alt="free" width="400"/><br>
|
||||
</p>
|
||||
|
||||
### If this start with Pro Trial, you can use it, don't hurry to upgrade to Pro, unless you can't use Pro, because Pro sometimes will be slow mode
|
||||
## ⚠️ Google Recaptcha need to be manually verified, don't be lazy, move your fingers, verify it, otherwise it will keep prompting you to verify ⚠️
|
||||
### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
||||
|
||||
### 如果啟動後是 Pro Trial 會員,可以使用就使用,不用著急升級到Pro 會員,除非不能使用Pro會員,因為Pro 會員有時候會 慢速模式
|
||||
## ⚠️ 郵箱驗證 需要手動驗證,不要那麼懶,動一動手指,驗證一下,不然會一直提示你驗證 ⚠️
|
||||
### 如果沒有Google Chrome,可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
|
||||
|
||||
##### If Using Pro Membership, if it is slow mode, please switch to gpt-4o-mini /cursor-slow /cursor-fast mode
|
||||
|
||||
##### 如果使用Pro會員,是慢速模式的話,請切換到gpt-4o-mini /cursor-slow /cursor-fast 模式
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
## 🔄 更新日志
|
||||
<details open>
|
||||
<summary>v1.0.5 - HotFix</summary>
|
||||
|
||||
1. Fix: Mac Browser Control | 修復Mac瀏覽器控制問題
|
||||
2. Fix: Verification Code Cant Patch | 修復驗證碼無法修復問題
|
||||
3. Add Linux Support | 增加Linux支持
|
||||
<p align="center">
|
||||
<img src="./images/fix_2025-01-14_21-30-43.png" alt="fix" width="400"/><br>
|
||||
</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>v1.0.5</summary>
|
||||
|
||||
1. Remove MachineID | 移除機器碼ID
|
||||
2. Change to automatic registration account | 全面改為自動註冊賬號
|
||||
3. Use your own exclusive new account | 使用自己獨享的新賬號
|
||||
4. Fully automatic reset machine configuration | 全面自動化重置機器配置
|
||||
<p align="center">
|
||||
<img src="./images/pro_2025-01-14_14-40-37.png" alt="Why" width="400"/><br>
|
||||
</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>v1.0.4</summary>
|
||||
|
||||
1. Fix: Cursor's configuration | 修復Cursor的配置問題
|
||||
@@ -101,13 +117,13 @@ This is a tool to automatically bypass Cursor's membership check, automatically
|
||||
|
||||
## ✨ Features | 功能特點
|
||||
|
||||
* Auto bypass Cursor's membership check<br>自動繞過Cursor的會員檢查<br>
|
||||
* Automatically register Cursor membership<br>自動註冊Cursor會員<br>
|
||||
|
||||
* Auto upgrade to "pro" membership<br>自動升級為 "pro" 會員<br>
|
||||
* Except for Google verification code<br>除了Google驗證碼<br>
|
||||
|
||||
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
|
||||
|
||||
* Real-time send Token request<br>實時發送Token請求<br>
|
||||
* Complete Auth verification<br>完成Auth驗證<br>
|
||||
|
||||
* Reset Cursor's configuration<br>重置Cursor的配置<br>
|
||||
|
||||
@@ -129,7 +145,7 @@ This is a tool to automatically bypass Cursor's membership check, automatically
|
||||
|
||||
**Linux/macOS**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && sudo ./install.sh
|
||||
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
|
||||
```
|
||||
|
||||
**Windows**
|
||||
@@ -138,7 +154,7 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/inst
|
||||
```
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<details>
|
||||
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
|
||||
|
||||
**Linux/macOS**
|
||||
@@ -174,7 +190,7 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
|
||||
|
||||
|
||||
|
||||
## 🤩 貢獻 | Contribution
|
||||
## 🤩 Contribution | 貢獻
|
||||
|
||||
歡迎提交 Issue 和 Pull Request!
|
||||
|
||||
@@ -184,4 +200,6 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
|
||||
|
||||
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br>
|
||||
|
||||
源代碼靈感來之 | Original code inspiration from [Here](https://github.com/hmhm2022/gpt-cursor-auto)
|
||||
|
||||
This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne by the user.
|
||||
|
||||
98
browser.py
Normal file
98
browser.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from DrissionPage import ChromiumOptions, ChromiumPage
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
|
||||
|
||||
class BrowserManager:
|
||||
def __init__(self, noheader=False):
|
||||
self.browser = None
|
||||
self.noheader = noheader
|
||||
|
||||
def init_browser(self):
|
||||
"""初始化浏览器"""
|
||||
co = self._get_browser_options()
|
||||
|
||||
# 如果设置了 noheader,添加相应的参数
|
||||
if self.noheader:
|
||||
co.set_argument('--headless=new')
|
||||
|
||||
self.browser = ChromiumPage(co)
|
||||
return self.browser
|
||||
|
||||
def _get_browser_options(self):
|
||||
"""获取浏览器配置"""
|
||||
co = ChromiumOptions()
|
||||
try:
|
||||
extension_path = self._get_extension_path()
|
||||
co.add_extension(extension_path)
|
||||
|
||||
extension_block_path = self.get_extension_block()
|
||||
co.add_extension(extension_block_path)
|
||||
|
||||
extension_recaptcha_path = self.get_extension_recaptcha()
|
||||
co.add_extension(extension_recaptcha_path)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
logging.warning(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_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 get_extension_block(self):
|
||||
"""获取插件路径"""
|
||||
root_dir = os.getcwd()
|
||||
extension_path = os.path.join(root_dir, "uBlock0.chromium")
|
||||
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
extension_path = os.path.join(sys._MEIPASS, "uBlock0.chromium")
|
||||
|
||||
if not os.path.exists(extension_path):
|
||||
raise FileNotFoundError(f"插件不存在: {extension_path}")
|
||||
|
||||
return extension_path
|
||||
|
||||
def get_extension_recaptcha(self):
|
||||
"""获取插件路径"""
|
||||
root_dir = os.getcwd()
|
||||
extension_path = os.path.join(root_dir, "recaptchaPatch")
|
||||
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
extension_path = os.path.join(sys._MEIPASS, "recaptchaPatch")
|
||||
|
||||
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
|
||||
62
build.bat
Normal file
62
build.bat
Normal file
@@ -0,0 +1,62 @@
|
||||
@echo off
|
||||
chcp 65001 > nul
|
||||
cls
|
||||
|
||||
:: 檢查是否以管理員權限運行
|
||||
net session >nul 2>&1
|
||||
if %errorLevel% == 0 (
|
||||
:: 如果是管理員權限,只創建虛擬環境後就降權運行
|
||||
if not exist venv (
|
||||
echo ℹ️ 正在創建虛擬環境...
|
||||
python -m venv venv
|
||||
)
|
||||
|
||||
:: 降權運行剩餘的步驟
|
||||
echo ℹ️ 以普通用戶權限繼續...
|
||||
powershell -Command "Start-Process -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0 run' -Verb RunAs:NO"
|
||||
exit /b
|
||||
) else (
|
||||
:: 檢查是否是第二階段運行
|
||||
if "%1"=="run" (
|
||||
goto RUN_BUILD
|
||||
) else (
|
||||
:: 如果是普通權限且需要創建虛擬環境,請求管理員權限
|
||||
if not exist venv (
|
||||
echo ⚠️ 需要管理員權限來創建虛擬環境
|
||||
echo ℹ️ 正在請求管理員權限...
|
||||
powershell -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0'"
|
||||
exit /b
|
||||
) else (
|
||||
goto RUN_BUILD
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
:RUN_BUILD
|
||||
echo ℹ️ 啟動虛擬環境...
|
||||
call venv\Scripts\activate.bat
|
||||
if errorlevel 1 (
|
||||
echo ❌ 啟動虛擬環境失敗
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: 檢查並安裝缺失的依賴
|
||||
echo ℹ️ 檢查依賴...
|
||||
for /f "tokens=1" %%i in (requirements.txt) do (
|
||||
pip show %%i >nul 2>&1 || (
|
||||
echo ℹ️ 安裝 %%i...
|
||||
pip install %%i
|
||||
)
|
||||
)
|
||||
|
||||
echo ℹ️ 開始構建...
|
||||
python build.py
|
||||
if errorlevel 1 (
|
||||
echo ❌ 構建失敗
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo ✅ 完成!
|
||||
pause
|
||||
33
build.mac.command
Normal file
33
build.mac.command
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
export PYTHONWARNINGS=ignore::SyntaxWarning:DrissionPage
|
||||
|
||||
# Get script directory
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
echo "Creating virtual environment..."
|
||||
|
||||
# Check if virtual environment exists
|
||||
if [ ! -d "venv" ]; then
|
||||
python3 -m venv venv
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to create virtual environment!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Activate virtual environment
|
||||
source venv/bin/activate
|
||||
|
||||
# Install dependencies
|
||||
echo "Installing dependencies..."
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Run build script
|
||||
echo "Starting build process..."
|
||||
python build.py
|
||||
|
||||
# Keep window open
|
||||
echo "Build completed!"
|
||||
echo "Press any key to exit..."
|
||||
read -n 1
|
||||
113
build.py
Normal file
113
build.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import warnings
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import time
|
||||
import threading
|
||||
import shutil
|
||||
from logo import print_logo
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# 忽略特定警告
|
||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||
|
||||
class LoadingAnimation:
|
||||
def __init__(self):
|
||||
self.is_running = False
|
||||
self.animation_thread = None
|
||||
|
||||
def start(self, message="Building"):
|
||||
self.is_running = True
|
||||
self.animation_thread = threading.Thread(target=self._animate, args=(message,))
|
||||
self.animation_thread.start()
|
||||
|
||||
def stop(self):
|
||||
self.is_running = False
|
||||
if self.animation_thread:
|
||||
self.animation_thread.join()
|
||||
print("\r" + " " * 70 + "\r", end="", flush=True)
|
||||
|
||||
def _animate(self, message):
|
||||
animation = "|/-\\"
|
||||
idx = 0
|
||||
while self.is_running:
|
||||
print(f"\r{message} {animation[idx % len(animation)]}", end="", flush=True)
|
||||
idx += 1
|
||||
time.sleep(0.1)
|
||||
|
||||
def progress_bar(progress, total, prefix="", length=50):
|
||||
filled = int(length * progress // total)
|
||||
bar = "█" * filled + "░" * (length - filled)
|
||||
percent = f"{100 * progress / total:.1f}"
|
||||
print(f"\r{prefix} |{bar}| {percent}% Complete", end="", flush=True)
|
||||
if progress == total:
|
||||
print()
|
||||
|
||||
def simulate_progress(message, duration=1.0, steps=20):
|
||||
print(f"\033[94m{message}\033[0m")
|
||||
for i in range(steps + 1):
|
||||
time.sleep(duration / steps)
|
||||
progress_bar(i, steps, prefix="Progress:", length=40)
|
||||
|
||||
def build():
|
||||
# 清理屏幕
|
||||
os.system("cls" if platform.system().lower() == "windows" else "clear")
|
||||
|
||||
# 顯示 logo
|
||||
print_logo()
|
||||
|
||||
# 清理 PyInstaller 緩存
|
||||
print("\033[93m🧹 清理構建緩存...\033[0m")
|
||||
if os.path.exists('build'):
|
||||
shutil.rmtree('build')
|
||||
|
||||
# 重新加載環境變量以確保獲取最新版本
|
||||
load_dotenv(override=True)
|
||||
version = os.getenv('VERSION', '1.0.0')
|
||||
print(f"\033[93m📦 正在構建版本: v{version}\033[0m")
|
||||
|
||||
try:
|
||||
simulate_progress("Preparing build environment...", 0.5)
|
||||
|
||||
loading = LoadingAnimation()
|
||||
loading.start("Building in progress")
|
||||
|
||||
# 根据系统类型设置输出名称
|
||||
system = platform.system().lower()
|
||||
if system == "windows":
|
||||
os_type = "windows"
|
||||
ext = ".exe"
|
||||
elif system == "linux":
|
||||
os_type = "linux"
|
||||
ext = ""
|
||||
else: # Darwin
|
||||
os_type = "mac"
|
||||
ext = ""
|
||||
|
||||
output_name = f"CursorFreeVIP_{version}_{os_type}"
|
||||
|
||||
# 构建命令
|
||||
build_command = f'pyinstaller --clean --noconfirm build.spec'
|
||||
output_path = os.path.join('dist', f'{output_name}{ext}')
|
||||
|
||||
os.system(build_command)
|
||||
|
||||
loading.stop()
|
||||
|
||||
if os.path.exists(output_path):
|
||||
print(f"\n\033[92m✅ 構建完成!")
|
||||
print(f"📦 可執行文件位於: {output_path}\033[0m")
|
||||
else:
|
||||
print("\n\033[91m❌ 構建失敗:未找到輸出文件\033[0m")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
if loading:
|
||||
loading.stop()
|
||||
print(f"\n\033[91m❌ 構建過程出錯: {str(e)}\033[0m")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
build()
|
||||
87
build.sh
Normal file
87
build.sh
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 检查并安装必要的依赖
|
||||
check_dependencies() {
|
||||
echo -e "${YELLOW}检查系统依赖...${NC}"
|
||||
|
||||
# 检查是否为 Ubuntu/Debian
|
||||
if [ -f /etc/debian_version ]; then
|
||||
# 检查并安装必要的包
|
||||
PACKAGES="python3 python3-pip python3-venv"
|
||||
for pkg in $PACKAGES; do
|
||||
if ! dpkg -l | grep -q "^ii $pkg "; then
|
||||
echo -e "${YELLOW}安装 $pkg...${NC}"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y $pkg
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo -e "${RED}不支持的系统,请手动安装 python3, pip3 和 python3-venv${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建并激活虚拟环境
|
||||
setup_venv() {
|
||||
echo -e "${GREEN}正在创建虚拟环境...${NC}"
|
||||
python3 -m venv venv
|
||||
|
||||
echo -e "${GREEN}启动虚拟环境...${NC}"
|
||||
. ./venv/bin/activate || source ./venv/bin/activate
|
||||
}
|
||||
|
||||
# 安装依赖
|
||||
install_dependencies() {
|
||||
echo -e "${GREEN}安装依赖...${NC}"
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install -r requirements.txt
|
||||
}
|
||||
|
||||
# 构建程序
|
||||
build_program() {
|
||||
echo -e "${GREEN}开始构建...${NC}"
|
||||
python3 build.py
|
||||
}
|
||||
|
||||
# 清理
|
||||
cleanup() {
|
||||
echo -e "${GREEN}清理虚拟环境...${NC}"
|
||||
deactivate 2>/dev/null || true
|
||||
rm -rf venv
|
||||
}
|
||||
|
||||
# 主程序
|
||||
main() {
|
||||
# 检查依赖
|
||||
check_dependencies
|
||||
|
||||
# 设置虚拟环境
|
||||
setup_venv
|
||||
|
||||
# 安装依赖
|
||||
install_dependencies
|
||||
|
||||
# 构建
|
||||
build_program
|
||||
|
||||
# 清理
|
||||
cleanup
|
||||
|
||||
echo -e "${GREEN}完成!${NC}"
|
||||
echo "按任意键退出..."
|
||||
# 使用兼容的方式读取输入
|
||||
if [ "$(uname)" = "Linux" ]; then
|
||||
read dummy
|
||||
else
|
||||
read -n 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 运行主程序
|
||||
main
|
||||
72
build.spec
Normal file
72
build.spec
Normal file
@@ -0,0 +1,72 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
import os
|
||||
import platform
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# 加載環境變量獲取版本號
|
||||
load_dotenv()
|
||||
version = os.getenv('VERSION', '1.0.0')
|
||||
|
||||
# 根据系统类型设置输出名称
|
||||
system = platform.system().lower()
|
||||
if system == "windows":
|
||||
os_type = "windows"
|
||||
elif system == "linux":
|
||||
os_type = "linux"
|
||||
else: # Darwin
|
||||
os_type = "mac"
|
||||
|
||||
output_name = f"CursorFreeVIP_{version}_{os_type}"
|
||||
|
||||
a = Analysis(
|
||||
['main.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[
|
||||
('turnstilePatch', 'turnstilePatch'),
|
||||
('recaptchaPatch', 'recaptchaPatch'),
|
||||
('uBlock0.chromium', 'uBlock0.chromium'),
|
||||
('cursor_auth.py', '.'),
|
||||
('reset_machine_manual.py', '.'),
|
||||
('cursor_register.py', '.'),
|
||||
('browser.py', '.'),
|
||||
('control.py', '.')
|
||||
],
|
||||
hiddenimports=[
|
||||
'cursor_auth',
|
||||
'reset_machine_manual',
|
||||
'browser',
|
||||
'control'
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
target_arch = os.environ.get('TARGET_ARCH', None)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name=output_name, # 使用动态生成的名称
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=True, # 对非Mac平台无影响
|
||||
target_arch=target_arch, # 仅在需要时通过环境变量指定
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon=None
|
||||
)
|
||||
378
control.py
Normal file
378
control.py
Normal file
@@ -0,0 +1,378 @@
|
||||
import time
|
||||
from colorama import Fore, Style
|
||||
import random
|
||||
import os
|
||||
|
||||
class BrowserControl:
|
||||
def __init__(self, browser):
|
||||
self.browser = browser
|
||||
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
|
||||
self.current_tab = None # 当前标签页
|
||||
self.signup_tab = None # 注册标签页
|
||||
self.email_tab = None # 邮箱标签页
|
||||
|
||||
def create_new_tab(self):
|
||||
"""创建新标签页"""
|
||||
try:
|
||||
# 保存当前标签页
|
||||
self.current_tab = self.browser
|
||||
|
||||
# 创建新的浏览器实例
|
||||
from browser import BrowserManager
|
||||
browser_manager = BrowserManager()
|
||||
new_browser = browser_manager.init_browser()
|
||||
|
||||
# 保存新标签页
|
||||
self.signup_tab = new_browser
|
||||
|
||||
print(f"{Fore.GREEN}Create New Tab Success | 成功创建新窗口{Style.RESET_ALL}")
|
||||
return new_browser
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Create New Tab Failed | 创建新窗口时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def switch_to_tab(self, browser):
|
||||
"""切换到指定浏览器窗口"""
|
||||
try:
|
||||
self.browser = browser
|
||||
print(f"{Fore.GREEN}Switch Tab Success | 成功切换窗口{Style.RESET_ALL}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Switch Tab Failed | 切换窗口时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_current_tab(self):
|
||||
"""获取当前标签页"""
|
||||
return self.browser
|
||||
|
||||
def generate_new_email(self):
|
||||
"""点击新的按钮生成新邮箱"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Click Generate New Email | 点击生成新邮箱...{Style.RESET_ALL}")
|
||||
new_button = self.browser.ele('xpath://button[contains(@class, "egenbut")]')
|
||||
if new_button:
|
||||
new_button.click()
|
||||
time.sleep(1) # 等待生成
|
||||
print(f"{Fore.GREEN}Generate New Email | 成功生成新邮箱{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}No Generate Button Found | 未找到生成按钮{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Generate New Email Failed | 生成新邮箱时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def select_email_domain(self, domain_index=None):
|
||||
"""选择邮箱域名,如果不指定index则随机选择"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Select Email Domain | 选择邮箱域名...{Style.RESET_ALL}")
|
||||
# 找到下拉框
|
||||
select_element = self.browser.ele('xpath://select[@id="seldom"]')
|
||||
if select_element:
|
||||
# 获取所有选项,包括两个 optgroup 下的所有 option
|
||||
all_options = []
|
||||
|
||||
# 获取 "新的" 组下的选项
|
||||
new_options = self.browser.eles('xpath://select[@id="seldom"]/optgroup[@label="-- 新的 --"]/option')
|
||||
all_options.extend(new_options)
|
||||
|
||||
# 获取 "其他" 组下的选项
|
||||
other_options = self.browser.eles('xpath://select[@id="seldom"]/optgroup[@label="-- 其他 --"]/option')
|
||||
all_options.extend(other_options)
|
||||
|
||||
if all_options:
|
||||
# 如果没有指定索引,随机选择一个
|
||||
if domain_index is None:
|
||||
domain_index = random.randint(0, len(all_options) - 1)
|
||||
|
||||
if domain_index < len(all_options):
|
||||
# 获取选中选项的文本
|
||||
selected_domain = all_options[domain_index].text
|
||||
print(f"{Fore.CYAN}Select Email Domain | 选择域名: {selected_domain}{Style.RESET_ALL}")
|
||||
|
||||
# 点击选择
|
||||
all_options[domain_index].click()
|
||||
time.sleep(1)
|
||||
print(f"{Fore.GREEN}Select Email Domain | 成功选择邮箱域名{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
print(f"{Fore.RED}No Available Domain Options | 未找到可用的域名选项,总共有 {len(all_options)} 个选项{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
print(f"{Fore.RED}No Domain Select Box Found | 未找到域名选择框{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Select Email Domain Failed | 选择邮箱域名时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def wait_for_page_load(self, seconds=2):
|
||||
"""等待页面加载"""
|
||||
time.sleep(seconds)
|
||||
|
||||
def navigate_to(self, url):
|
||||
"""导航到指定URL"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Navigate to {url} | 正在访问 {url}...{Style.RESET_ALL}")
|
||||
self.browser.get(url)
|
||||
self.wait_for_page_load()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Visit {url} Failed | 访问 {url} 时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def copy_and_get_email(self):
|
||||
"""获取邮箱地址"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Get Email Info | 获取邮箱信息...{Style.RESET_ALL}")
|
||||
|
||||
# 等待元素加载
|
||||
time.sleep(1)
|
||||
|
||||
# 获取邮箱名称
|
||||
try:
|
||||
email_div = self.browser.ele('xpath://div[@class="segen"]//div[contains(@style, "color: #e5e5e5")]')
|
||||
if email_div:
|
||||
email_name = email_div.text.split()[0]
|
||||
print(f"{Fore.CYAN}Get Email Name | 找到邮箱名称: {email_name}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}Get Email Name Failed | 无法找到邮箱名称元素{Style.RESET_ALL}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Get Email Name Failed | 获取邮箱名称时出错: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
# 直接使用上一步选择的域名
|
||||
try:
|
||||
domain = self.browser.ele('xpath://select[@id="seldom"]').value
|
||||
if not domain: # 如果获取不到value,尝试获取选中的选项文本
|
||||
selected_option = self.browser.ele('xpath://select[@id="seldom"]/option[1]')
|
||||
domain = selected_option.text if selected_option else "@yopmail.com" # 使用默认域名作为后备
|
||||
except:
|
||||
domain = "@yopmail.com" # 如果出错,使用默认域名
|
||||
|
||||
# 组合完整邮箱地址
|
||||
full_email = f"{email_name}{domain}"
|
||||
print(f"{Fore.GREEN}Get Email Address | 完整邮箱地址: {full_email}{Style.RESET_ALL}")
|
||||
return full_email
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Get Email Address Failed | 获取邮箱地址时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def view_mailbox(self):
|
||||
"""点击查看邮箱按钮"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Enter Mailbox | 正在进入邮箱...{Style.RESET_ALL}")
|
||||
view_button = self.browser.ele('xpath://button[contains(@class, "egenbut") and contains(.//span, "查看邮箱")]')
|
||||
if view_button:
|
||||
view_button.click()
|
||||
time.sleep(2) # 等待页面加载
|
||||
print(f"{Fore.GREEN}Successfully Entered Mailbox | 成功进入邮箱{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}No View Mailbox Button Found | 未找到查看邮箱按钮{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Enter Mailbox Failed | 进入邮箱时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def refresh_mailbox(self):
|
||||
"""刷新邮箱获取最新信息"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Refresh Mailbox | 正在刷新邮箱...{Style.RESET_ALL}")
|
||||
refresh_button = self.browser.ele('xpath://button[@id="refresh"]')
|
||||
if refresh_button:
|
||||
refresh_button.click()
|
||||
time.sleep(2) # 等待刷新完成
|
||||
print(f"{Fore.GREEN}Mailbox Refreshed Successfully | 邮箱刷新成功{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
print(f"{Fore.RED}No Refresh Button Found | 未找到刷新按钮{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Refresh Mailbox Failed | 刷新邮箱时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_and_click_recaptcha(self):
|
||||
"""检查并点击验证码复选框"""
|
||||
try:
|
||||
# 使用环境变量或配置文件中预设的坐标
|
||||
click_x = int(os.getenv('RECAPTCHA_X', '100')) # 默认值100
|
||||
click_y = int(os.getenv('RECAPTCHA_Y', '100')) # 默认值100
|
||||
|
||||
print(f"{Fore.CYAN}使用预设坐标点击: x={click_x}, y={click_y}{Style.RESET_ALL}")
|
||||
|
||||
# 直接点击预设坐标
|
||||
self.browser.page.mouse.click(click_x, click_y)
|
||||
print(f"{Fore.GREEN}Clicked reCAPTCHA Position | 已点击 reCAPTCHA 位置{Style.RESET_ALL}")
|
||||
time.sleep(1)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}Click reCAPTCHA Failed | 点击 reCAPTCHA 失败: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_verification_code(self):
|
||||
"""从邮件中获取验证码"""
|
||||
try:
|
||||
# 尝试所有可能的样式组合
|
||||
selectors = [
|
||||
# 新样式
|
||||
'xpath://div[contains(@style, "font-family:-apple-system") and contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "color:#202020")]',
|
||||
# 带行高的样式
|
||||
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "line-height:30px")]',
|
||||
# rgba 颜色样式
|
||||
'xpath://div[contains(@style, "font-size: 28px") and contains(@style, "letter-spacing: 2px") and contains(@style, "color: rgba(32, 32, 32, 1)")]',
|
||||
# 宽松样式
|
||||
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]'
|
||||
]
|
||||
|
||||
# 依次尝试每个选择器
|
||||
for selector in selectors:
|
||||
code_div = self.browser.ele(selector)
|
||||
if code_div:
|
||||
verification_code = code_div.text.strip()
|
||||
if verification_code.isdigit() and len(verification_code) == 6:
|
||||
print(f"{Fore.GREEN}Found Verification Code | 找到验证码: {verification_code}{Style.RESET_ALL}")
|
||||
return verification_code
|
||||
|
||||
print(f"{Fore.YELLOW}No Valid Verification Code Found | 未找到有效验证码{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Get Verification Code Error | 获取验证码时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def fill_verification_code(self, code):
|
||||
"""填写验证码"""
|
||||
try:
|
||||
if not code or len(code) != 6:
|
||||
print(f"{Fore.RED}Verification Code Format Error | 验证码格式不正确{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}Fill Verification Code | 正在填写验证码...{Style.RESET_ALL}")
|
||||
|
||||
# 记住当前标签页(邮箱页面)
|
||||
email_tab = self.browser
|
||||
|
||||
# 切换回注册页面标签
|
||||
self.switch_to_tab(self.signup_tab)
|
||||
time.sleep(1)
|
||||
|
||||
# 输入验证码
|
||||
for digit in code:
|
||||
self.browser.actions.input(digit)
|
||||
time.sleep(random.uniform(0.1, 0.3))
|
||||
|
||||
print(f"{Fore.GREEN}Verification Code Filled | 验证码填写完成{Style.RESET_ALL}")
|
||||
|
||||
# 等待页面加载和登录完成
|
||||
print(f"{Fore.CYAN}Wait for Login | 等待登录完成...{Style.RESET_ALL}")
|
||||
time.sleep(5)
|
||||
|
||||
# 先访问登录页面确保登录状态
|
||||
login_url = "https://authenticator.cursor.sh"
|
||||
self.browser.get(login_url)
|
||||
time.sleep(3) # 增加等待时间
|
||||
|
||||
# 获取cookies(第一次尝试)
|
||||
token = self.get_cursor_session_token()
|
||||
if not token:
|
||||
print(f"{Fore.YELLOW}Get Token Failed | 首次获取token失败,等待后重试...{Style.RESET_ALL}")
|
||||
time.sleep(3)
|
||||
token = self.get_cursor_session_token()
|
||||
|
||||
if token:
|
||||
self.save_token_to_file(token)
|
||||
|
||||
# 获取到token后再访问设置页面
|
||||
settings_url = "https://www.cursor.com/settings"
|
||||
print(f"{Fore.CYAN}Get Account Info | 正在访问设置页面获取账户信息...{Style.RESET_ALL}")
|
||||
self.browser.get(settings_url)
|
||||
time.sleep(2)
|
||||
|
||||
# 获取账户额度信息
|
||||
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 = self.browser.ele(usage_selector)
|
||||
if usage_ele:
|
||||
usage_info = usage_ele.text
|
||||
total_usage = usage_info.split("/")[-1].strip()
|
||||
print(f"{Fore.GREEN}Account Usage Limit | 账户可用额度上限: {total_usage}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Get Account Usage Failed | 获取账户额度信息失败: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
# 切换回邮箱页面
|
||||
self.switch_to_tab(email_tab)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Fill Verification Code Failed | 填写验证码时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def check_and_click_turnstile(self):
|
||||
"""检查并点击 Turnstile 验证框"""
|
||||
try:
|
||||
# 等待验证框出现
|
||||
time.sleep(1)
|
||||
|
||||
# 查找验证框
|
||||
verify_checkbox = self.browser.ele('xpath://label[contains(@class, "cb-lb")]//input[@type="checkbox"]')
|
||||
if verify_checkbox:
|
||||
print(f"{Fore.CYAN}Find Turnstile Verification Box | 找到 Turnstile 验证框,尝试点击...{Style.RESET_ALL}")
|
||||
verify_checkbox.click()
|
||||
time.sleep(2) # 等待验证完成
|
||||
print(f"{Fore.GREEN}Clicked Turnstile Verification Box | 已点击 Turnstile 验证框{Style.RESET_ALL}")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}Check and Click Turnstile Failed | 未找到 Turnstile 验证框或点击失败: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def get_cursor_session_token(self, max_attempts=3, retry_interval=2):
|
||||
"""获取Cursor会话token"""
|
||||
print(f"{Fore.CYAN}Get Cursor Session Token | 开始获取cookie...{Style.RESET_ALL}")
|
||||
attempts = 0
|
||||
|
||||
while attempts < max_attempts:
|
||||
try:
|
||||
# 直接从浏览器对象获取cookies
|
||||
all_cookies = self.browser.get_cookies()
|
||||
|
||||
# 遍历查找目标cookie
|
||||
for cookie in all_cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
token = cookie["value"].split("%3A%3A")[1]
|
||||
print(f"{Fore.GREEN}成功获取CursorSessionToken: {token}{Style.RESET_ALL}")
|
||||
return token
|
||||
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW} Try | 第 {attempts} 次尝试未获取到CursorSessionToken,{retry_interval}秒后重试...{Style.RESET_ALL}")
|
||||
time.sleep(retry_interval)
|
||||
else:
|
||||
print(f"{Fore.RED}Reach Max Attempts ({max_attempts}) | 已达到最大尝试次数({max_attempts}),获取CursorSessionToken失败{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}获取cookie失败: {str(e)}{Style.RESET_ALL}")
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW}Will Retry in {retry_interval} seconds | 将在 {retry_interval} 秒后重试...{Style.RESET_ALL}")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
return None
|
||||
|
||||
def save_token_to_file(self, token):
|
||||
"""保存token到文件"""
|
||||
try:
|
||||
with open('cursor_tokens.txt', 'a', encoding='utf-8') as f:
|
||||
f.write(f"Token: {token}\n")
|
||||
f.write("-" * 50 + "\n")
|
||||
print(f"{Fore.GREEN}Token Saved to cursor_tokens.txt | Token已保存到 cursor_tokens.txt{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Save Token Failed | 保存Token时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
112
cursor_auth.py
Normal file
112
cursor_auth.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import sqlite3
|
||||
import os
|
||||
import sys
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# 初始化colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
EMOJI = {
|
||||
'DB': '🗄️',
|
||||
'UPDATE': '🔄',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WARN': '⚠️',
|
||||
'INFO': 'ℹ️',
|
||||
'KEY': '🔑'
|
||||
}
|
||||
|
||||
class CursorAuth:
|
||||
def __init__(self):
|
||||
# 判断操作系统
|
||||
if os.name == "nt": # Windows
|
||||
self.db_path = os.path.join(
|
||||
os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb"
|
||||
)
|
||||
else: # macOS
|
||||
self.db_path = os.path.expanduser(
|
||||
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
||||
)
|
||||
|
||||
def update_auth(self, email=None, access_token=None, refresh_token=None):
|
||||
conn = None
|
||||
try:
|
||||
# 确保目录存在并设置正确权限
|
||||
db_dir = os.path.dirname(self.db_path)
|
||||
if not os.path.exists(db_dir):
|
||||
os.makedirs(db_dir, mode=0o755, exist_ok=True)
|
||||
|
||||
# 如果数据库文件不存在,创建一个新的
|
||||
if not os.path.exists(self.db_path):
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS ItemTable (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
if sys.platform != "win32":
|
||||
os.chmod(self.db_path, 0o644)
|
||||
conn.close()
|
||||
|
||||
# 重新连接数据库
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
print(f"{EMOJI['INFO']} {Fore.GREEN}Successfully connected to database | 成功连接到数据库{Style.RESET_ALL}")
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 增加超时和其他优化设置
|
||||
conn.execute("PRAGMA busy_timeout = 5000")
|
||||
conn.execute("PRAGMA journal_mode = WAL")
|
||||
conn.execute("PRAGMA synchronous = NORMAL")
|
||||
|
||||
# 设置要更新的键值对
|
||||
updates = []
|
||||
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))
|
||||
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
|
||||
|
||||
# 使用事务来确保数据完整性
|
||||
cursor.execute("BEGIN TRANSACTION")
|
||||
try:
|
||||
for key, value in updates:
|
||||
# 检查键是否存在
|
||||
cursor.execute("SELECT COUNT(*) FROM ItemTable WHERE key = ?", (key,))
|
||||
if cursor.fetchone()[0] == 0:
|
||||
cursor.execute("""
|
||||
INSERT INTO ItemTable (key, value)
|
||||
VALUES (?, ?)
|
||||
""", (key, value))
|
||||
else:
|
||||
cursor.execute("""
|
||||
UPDATE ItemTable SET value = ?
|
||||
WHERE key = ?
|
||||
""", (value, key))
|
||||
print(f"{EMOJI['INFO']} {Fore.CYAN}Updating {key.split('/')[-1]}...{Style.RESET_ALL}")
|
||||
|
||||
cursor.execute("COMMIT")
|
||||
print(f"{EMOJI['SUCCESS']} {Fore.GREEN}Database updated successfully | 数据库更新成功{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
cursor.execute("ROLLBACK")
|
||||
raise e
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"\n{EMOJI['ERROR']} {Fore.RED}Database error | 数据库错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"\n{EMOJI['ERROR']} {Fore.RED}An error occurred | 发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
print(f"{EMOJI['DB']} {Fore.CYAN}Database connection closed | 数据库连接已关闭{Style.RESET_ALL}")
|
||||
|
||||
|
||||
415
cursor_register.py
Normal file
415
cursor_register.py
Normal file
@@ -0,0 +1,415 @@
|
||||
import os
|
||||
from colorama import Fore, Style, init
|
||||
import time
|
||||
import random
|
||||
from browser import BrowserManager
|
||||
from control import BrowserControl
|
||||
from cursor_auth import CursorAuth
|
||||
from reset_machine_manual import MachineIDResetter
|
||||
|
||||
os.environ["PYTHONVERBOSE"] = "0"
|
||||
os.environ["PYINSTALLER_VERBOSE"] = "0"
|
||||
|
||||
# 初始化colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'FORM': '📝',
|
||||
'VERIFY': '🔄',
|
||||
'PASSWORD': '🔑',
|
||||
'CODE': '📱',
|
||||
'DONE': '✨',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'SUCCESS': '✅',
|
||||
'MAIL': '<EFBFBD><EFBFBD>',
|
||||
'KEY': '🔐',
|
||||
'UPDATE': '🔄'
|
||||
}
|
||||
|
||||
class CursorRegistration:
|
||||
def __init__(self):
|
||||
# 设置为显示模式
|
||||
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||
self.browser_manager = BrowserManager()
|
||||
self.browser = None
|
||||
self.controller = None
|
||||
self.mail_url = "https://yopmail.com/zh/email-generator"
|
||||
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
|
||||
self.settings_url = "https://www.cursor.com/settings"
|
||||
self.email_address = None
|
||||
self.signup_tab = None
|
||||
self.email_tab = None
|
||||
|
||||
# 账号信息
|
||||
self.password = self._generate_password()
|
||||
self.first_name = self._generate_name()
|
||||
self.last_name = self._generate_name()
|
||||
|
||||
def _generate_password(self, length=12):
|
||||
"""Generate Random Password"""
|
||||
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
|
||||
return ''.join(random.choices(chars, k=length))
|
||||
|
||||
def _generate_name(self, length=6):
|
||||
"""Generate Random Name"""
|
||||
first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
rest_letters = ''.join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length-1))
|
||||
return first_letter + rest_letters
|
||||
|
||||
def setup_email(self):
|
||||
"""Setup Temporary Email"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}Staring Browser | 正在启动浏览器...{Style.RESET_ALL}")
|
||||
self.browser = self.browser_manager.init_browser()
|
||||
self.controller = BrowserControl(self.browser)
|
||||
|
||||
# 打开邮箱生成器页面(第一个标签页)
|
||||
self.controller.navigate_to(self.mail_url)
|
||||
self.email_tab = self.browser # 保存邮箱标签页
|
||||
self.controller.email_tab = self.email_tab # 同时保存到controller
|
||||
|
||||
# 生成新邮箱
|
||||
self.controller.generate_new_email()
|
||||
|
||||
# 选择随机域名
|
||||
self.controller.select_email_domain()
|
||||
|
||||
# 获取邮箱地址
|
||||
self.email_address = self.controller.copy_and_get_email()
|
||||
if self.email_address:
|
||||
print(f"{Fore.CYAN}Get Email Address | 获取到的邮箱地址: {self.email_address}{Style.RESET_ALL}")
|
||||
|
||||
# 进入邮箱
|
||||
if self.controller.view_mailbox():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}Error Occured | 发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def register_cursor(self):
|
||||
"""注册 Cursor 账号"""
|
||||
signup_browser_manager = None
|
||||
try:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['START']}Start Register | 开始 Cursor 注册流程{Style.RESET_ALL}")
|
||||
|
||||
# 创建新的浏览器实例用于注册
|
||||
from browser import BrowserManager
|
||||
signup_browser_manager = BrowserManager(noheader=True)
|
||||
self.signup_tab = signup_browser_manager.init_browser()
|
||||
|
||||
# 访问注册页面
|
||||
self.signup_tab.get(self.sign_up_url)
|
||||
time.sleep(2)
|
||||
|
||||
# 填写注册表单
|
||||
if self.signup_tab.ele("@name=first_name"):
|
||||
print(f"{Fore.YELLOW}{EMOJI['FORM']}Fill Form | 填写注册信息...{Style.RESET_ALL}")
|
||||
|
||||
self.signup_tab.ele("@name=first_name").input(self.first_name)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
self.signup_tab.ele("@name=last_name").input(self.last_name)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
self.signup_tab.ele("@name=email").input(self.email_address)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
self.signup_tab.ele("@type=submit").click()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Basic Info Submitted | 基本信息提交完成{Style.RESET_ALL}")
|
||||
|
||||
# 处理 Turnstile 验证
|
||||
self._handle_turnstile()
|
||||
|
||||
# 设置密码
|
||||
if self.signup_tab.ele("@name=password"):
|
||||
print(f"{Fore.YELLOW}{EMOJI['PASSWORD']} Set Password | 设置密码...{Style.RESET_ALL}")
|
||||
self.signup_tab.ele("@name=password").input(self.password)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
self.signup_tab.ele("@type=submit").click()
|
||||
|
||||
self._handle_turnstile()
|
||||
|
||||
# 等待并获取验证码
|
||||
time.sleep(5) # 等待验证码邮件
|
||||
|
||||
self.browser.refresh()
|
||||
|
||||
# 获取验证码,设置60秒超时
|
||||
verification_code = None
|
||||
max_attempts = 10 # 增加到10次尝试
|
||||
retry_interval = 5 # 每5秒重试一次
|
||||
start_time = time.time()
|
||||
timeout = 60 # 60秒超时
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} Start Getting Verification Code | 开始获取验证码,将在60秒内尝试...{Style.RESET_ALL}")
|
||||
|
||||
for attempt in range(max_attempts):
|
||||
# 检查是否超时
|
||||
if time.time() - start_time > timeout:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Get Verification Code Timeout | 获取验证码超时{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
verification_code = self.controller.get_verification_code()
|
||||
if verification_code:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Get Verification Code Success | 成功获取验证码: {verification_code}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
remaining_time = int(timeout - (time.time() - start_time))
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Try | 第 {attempt + 1} Get Verification Code | 次尝试未获取到验证码,Time Remaining | 剩余时间: {remaining_time}秒{Style.RESET_ALL}")
|
||||
|
||||
# 刷新邮箱
|
||||
self.browser.refresh()
|
||||
time.sleep(retry_interval)
|
||||
|
||||
if verification_code:
|
||||
# 在注册页面填写验证码
|
||||
for i, digit in enumerate(verification_code):
|
||||
self.signup_tab.ele(f"@data-index={i}").input(digit)
|
||||
time.sleep(random.uniform(0.1, 0.3))
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Verification Code Filled | 验证码填写完成{Style.RESET_ALL}")
|
||||
time.sleep(3)
|
||||
|
||||
self._handle_turnstile()
|
||||
|
||||
# 检查当前URL
|
||||
current_url = self.signup_tab.url
|
||||
if "authenticator.cursor.sh" in current_url:
|
||||
print(f"{Fore.CYAN}{EMOJI['VERIFY']} Detect Login Page | 检测到登录页面,开始登录...{Style.RESET_ALL}")
|
||||
|
||||
# 填写邮箱
|
||||
email_input = self.signup_tab.ele('@name=email')
|
||||
if email_input:
|
||||
email_input.input(self.email_address)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
# 点击提交
|
||||
submit_button = self.signup_tab.ele('@type=submit')
|
||||
if submit_button:
|
||||
submit_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# 处理 Turnstile 验证
|
||||
self._handle_turnstile()
|
||||
|
||||
# 填写密码
|
||||
password_input = self.signup_tab.ele('@name=password')
|
||||
if password_input:
|
||||
password_input.input(self.password)
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
# 点击提交
|
||||
submit_button = self.signup_tab.ele('@type=submit')
|
||||
if submit_button:
|
||||
submit_button.click()
|
||||
time.sleep(2)
|
||||
|
||||
# 处理 Turnstile 验证
|
||||
self._handle_turnstile()
|
||||
|
||||
# 等待跳转到设置页面
|
||||
max_wait = 30
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < max_wait:
|
||||
if "cursor.com/settings" in self.signup_tab.url:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Login Success and Jump to Settings Page | 成功登录并跳转到设置页面{Style.RESET_ALL}")
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
# 获取账户信息
|
||||
result = self._get_account_info()
|
||||
|
||||
# 关闭注册窗口
|
||||
if signup_browser_manager:
|
||||
signup_browser_manager.quit()
|
||||
|
||||
return result
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Get Verification Code Timeout | 未能在60秒内获取到验证码{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Register Process Error | 注册过程出错: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
finally:
|
||||
# 确保在任何情况下都关闭注册窗口
|
||||
if signup_browser_manager:
|
||||
signup_browser_manager.quit()
|
||||
|
||||
def _handle_turnstile(self):
|
||||
"""处理 Turnstile 验证"""
|
||||
print(f"{Fore.YELLOW}{EMOJI['VERIFY']} Handle Turnstile | 处理 Turnstile 验证...{Style.RESET_ALL}")
|
||||
|
||||
# 设置最大等待时间(秒)
|
||||
max_wait_time = 10 # 增加等待时间
|
||||
start_time = time.time()
|
||||
|
||||
while True:
|
||||
try:
|
||||
# 检查是否超时
|
||||
if time.time() - start_time > max_wait_time:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Not Detect Turnstile | 未检测到 Turnstile 验证,继续下一步...{Style.RESET_ALL}")
|
||||
time.sleep(2) # 添加短暂延迟
|
||||
break
|
||||
|
||||
# 检查是否存在验证框
|
||||
try:
|
||||
challengeCheck = (
|
||||
self.signup_tab.ele("@id=cf-turnstile", timeout=1)
|
||||
.child()
|
||||
.shadow_root.ele("tag:iframe")
|
||||
.ele("tag:body")
|
||||
.sr("tag:input")
|
||||
)
|
||||
|
||||
if challengeCheck:
|
||||
challengeCheck.click()
|
||||
time.sleep(3) # 增加点击后的等待时间
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Turnstile Passed | 验证通过{Style.RESET_ALL}")
|
||||
time.sleep(2) # 确保验证完全完成
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
# 检查是否已经通过验证(检查下一步的元素是否存在)
|
||||
try:
|
||||
if (self.signup_tab.ele("@name=password", timeout=0.5) or
|
||||
self.signup_tab.ele("@name=email", timeout=0.5) or
|
||||
self.signup_tab.ele("@data-index=0", timeout=0.5)):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Verification Passed | 验证已通过{Style.RESET_ALL}")
|
||||
time.sleep(2) # 添加短暂延迟
|
||||
break
|
||||
except:
|
||||
pass
|
||||
|
||||
# 等待短暂时间后继续检查
|
||||
time.sleep(1)
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Turnstile Error | Turnstile 处理出错: {str(e)}{Style.RESET_ALL}")
|
||||
time.sleep(2) # 出错时等待更长时间
|
||||
break
|
||||
|
||||
# 最后再等待一下,确保页面加载完成
|
||||
time.sleep(2)
|
||||
|
||||
def _get_account_info(self):
|
||||
"""获取账户信息和 Token"""
|
||||
try:
|
||||
# 访问设置页面
|
||||
self.signup_tab.get(self.settings_url)
|
||||
time.sleep(2)
|
||||
|
||||
# 获取账户额度信息
|
||||
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 = self.signup_tab.ele(usage_selector)
|
||||
total_usage = "未知"
|
||||
if usage_ele:
|
||||
total_usage = usage_ele.text.split("/")[-1].strip()
|
||||
|
||||
# 获取 Token
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} Get Cursor Session Token | 开始获取 Cursor Session Token...{Style.RESET_ALL}")
|
||||
max_attempts = 30
|
||||
retry_interval = 2
|
||||
attempts = 0
|
||||
|
||||
while attempts < max_attempts:
|
||||
try:
|
||||
cookies = self.signup_tab.cookies()
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
token = cookie["value"].split("%3A%3A")[1]
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Get Token Success | Token 获取成功{Style.RESET_ALL}")
|
||||
# 保存账户信息
|
||||
self._save_account_info(token, total_usage)
|
||||
return True
|
||||
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(
|
||||
f"{Fore.YELLOW}{EMOJI['WAIT']} Try | 第 {attempts} times to get Token | 次尝试未获取到 Token,{retry_interval}秒后重试...{Style.RESET_ALL}"
|
||||
)
|
||||
time.sleep(retry_interval)
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Reach Max Attempts ({max_attempts}) | 已达到最大尝试次数({max_attempts}),获取 Token 失败{Style.RESET_ALL}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Get Token Failed | 获取 Token 失败: {str(e)}{Style.RESET_ALL}")
|
||||
attempts += 1
|
||||
if attempts < max_attempts:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Will Retry in {retry_interval} seconds | 将在 {retry_interval} 秒后重试...{Style.RESET_ALL}")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Get Account Info Failed | 获取账户信息失败: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def _save_account_info(self, token, total_usage):
|
||||
"""保存账户信息到文件"""
|
||||
try:
|
||||
# 先更新认证信息
|
||||
print(f"{Fore.CYAN}{EMOJI['KEY']} 正在更新 Cursor 认证信息...{Style.RESET_ALL}")
|
||||
if update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor Auth Info Updated | 认证信息更新成功{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Cursor Auth Info Update Failed | 认证信息更新失败{Style.RESET_ALL}")
|
||||
|
||||
# 重置机器ID
|
||||
print(f"{Fore.CYAN}{EMOJI['UPDATE']} Reset Machine ID | 正在重置机器ID...{Style.RESET_ALL}")
|
||||
MachineIDResetter().reset_machine_ids()
|
||||
|
||||
# 保存账户信息到文件
|
||||
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
|
||||
f.write(f"\n{'='*50}\n")
|
||||
f.write(f"Email: {self.email_address}\n")
|
||||
f.write(f"Password: {self.password}\n")
|
||||
f.write(f"Token: {token}\n")
|
||||
f.write(f"Usage Limit: {total_usage}\n")
|
||||
f.write(f"{'='*50}\n")
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account Info Saved to cursor_accounts.txt | 账户信息已保存到 cursor_accounts.txt{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Save Account Info Failed | 保存账户信息失败: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def start(self):
|
||||
"""启动注册流程"""
|
||||
try:
|
||||
if self.setup_email():
|
||||
if self.register_cursor():
|
||||
print(f"\n{Fore.GREEN}{EMOJI['DONE']} Cursor Registration Completed | 注册完成!{Style.RESET_ALL}")
|
||||
return True
|
||||
return False
|
||||
finally:
|
||||
if self.browser_manager:
|
||||
self.browser_manager.quit()
|
||||
|
||||
|
||||
|
||||
def update_cursor_auth(email=None, access_token=None, refresh_token=None):
|
||||
"""
|
||||
更新Cursor的认证信息的便捷函数
|
||||
"""
|
||||
auth_manager = CursorAuth()
|
||||
return auth_manager.update_auth(email, access_token, refresh_token)
|
||||
|
||||
def main():
|
||||
registration = CursorRegistration()
|
||||
registration.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
images/fix_2025-01-14_21-30-43.png
Normal file
BIN
images/fix_2025-01-14_21-30-43.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 252 KiB |
BIN
images/free_2025-01-14_14-59-15.png
Normal file
BIN
images/free_2025-01-14_14-59-15.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 546 KiB |
BIN
images/pro_2025-01-14_14-40-37.png
Normal file
BIN
images/pro_2025-01-14_14-40-37.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
25
logo.py
Normal file
25
logo.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from colorama import Fore, Style, init
|
||||
# 初始化 colorama
|
||||
init()
|
||||
|
||||
CURSOR_LOGO = f"""
|
||||
{Fore.CYAN}
|
||||
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗
|
||||
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ ██╔══██╗██╔══██╗██╔═══██╗
|
||||
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ ██████╔╝██████╔╝██║ ██║
|
||||
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ ██╔═══╝ ██╔══██╗██║ ██║
|
||||
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ ██║ ██║ ██║╚██████╔╝
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
|
||||
{Fore.YELLOW}
|
||||
Pro Version Activator
|
||||
{Fore.GREEN}
|
||||
Author: Pin Studios | yeongpin
|
||||
{Style.RESET_ALL}
|
||||
"""
|
||||
|
||||
def print_logo():
|
||||
print(CURSOR_LOGO)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print_logo()
|
||||
73
main.py
Normal file
73
main.py
Normal file
@@ -0,0 +1,73 @@
|
||||
# main.py
|
||||
# This script allows the user to choose which script to run.
|
||||
from logo import print_logo
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# 初始化colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"MENU": "📋",
|
||||
"ARROW": "➜",
|
||||
}
|
||||
|
||||
def print_menu():
|
||||
"""打印菜单选项"""
|
||||
print(f"\n{Fore.CYAN}{EMOJI['MENU']} Available Options | 可用选项:{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} Exit Program | 退出程序")
|
||||
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} Reset Machine Manual | 重置机器标识")
|
||||
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} Register Cursor | 注册 Cursor")
|
||||
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['ERROR']} Quit Cursor | 退出 Cursor")
|
||||
# 在这里添加更多选项
|
||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||
|
||||
def main():
|
||||
print_logo()
|
||||
print_menu()
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}Enter your choice (0-3) | 输入选择 (0-3): {Style.RESET_ALL}")
|
||||
|
||||
if choice == "0":
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} Exiting program... | 正在退出程序...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return # 直接返回,不等待按键
|
||||
elif choice == "1":
|
||||
import reset_machine_manual
|
||||
reset_machine_manual.run()
|
||||
break
|
||||
elif choice == "2":
|
||||
import cursor_register
|
||||
cursor_register.main()
|
||||
break
|
||||
elif choice == "3":
|
||||
import quit_cursor
|
||||
quit_cursor.quit_cursor()
|
||||
break
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid choice. Please try again | 无效选择,请重试{Style.RESET_ALL}")
|
||||
print_menu()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} Program terminated by user | 程序被用户终止{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
return # 直接返回,不等待按键
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} An error occurred | 发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
# 只有在执行完其他选项后才显示按键退出提示
|
||||
print(f"\n{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} Press Enter to Exit | 按回车键退出...{Style.RESET_ALL}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
88
quit_cursor.py
Normal file
88
quit_cursor.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import psutil
|
||||
import time
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# 初始化colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"PROCESS": "⚙️",
|
||||
"WAIT": "⏳"
|
||||
}
|
||||
|
||||
class CursorQuitter:
|
||||
def __init__(self, timeout=5):
|
||||
self.timeout = timeout
|
||||
|
||||
def quit_cursor(self):
|
||||
"""温和地关闭 Cursor 进程"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['PROCESS']} Start Quitting Cursor | 开始退出 Cursor...{Style.RESET_ALL}")
|
||||
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:
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} No Running Cursor Process | 未发现运行中的 Cursor 进程{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
# 温和地请求进程终止
|
||||
for proc in cursor_processes:
|
||||
try:
|
||||
if proc.is_running():
|
||||
print(f"{Fore.YELLOW}{EMOJI['PROCESS']} Terminating Process | 正在终止进程 {proc.pid}...{Style.RESET_ALL}")
|
||||
proc.terminate() # 发送终止信号
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
|
||||
# 等待进程自然终止
|
||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} Waiting for Process to Exit | 等待进程退出...{Style.RESET_ALL}")
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < self.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:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} All Cursor Processes Closed | 所有 Cursor 进程已正常关闭{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
# 等待一小段时间再检查
|
||||
time.sleep(0.5)
|
||||
|
||||
# 如果超时后仍有进程在运行
|
||||
if still_running:
|
||||
process_list = ", ".join([str(p.pid) for p in still_running])
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Process Timeout | 以下进程未能在规定时间内关闭: {process_list}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Error Occurred | 关闭 Cursor 进程时发生错误: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def quit_cursor(timeout=5):
|
||||
"""便捷函数,用于直接调用退出功能"""
|
||||
quitter = CursorQuitter(timeout)
|
||||
return quitter.quit_cursor()
|
||||
|
||||
if __name__ == "__main__":
|
||||
quit_cursor()
|
||||
22
recaptchaPatch/manifest.json
Normal file
22
recaptchaPatch/manifest.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Recaptcha Blocker",
|
||||
"version": "1.0",
|
||||
"description": "Blocks reCAPTCHA from loading",
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["*://yopmail.com/zh/wm*"],
|
||||
"js": ["script.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
"webNavigation",
|
||||
"activeTab"
|
||||
],
|
||||
"host_permissions": [
|
||||
"*://yopmail.com/*",
|
||||
"*://*.google.com/*"
|
||||
]
|
||||
}
|
||||
123
recaptchaPatch/script.js
Normal file
123
recaptchaPatch/script.js
Normal file
@@ -0,0 +1,123 @@
|
||||
// Debug logging function
|
||||
function log(message) {
|
||||
console.log(`[reCAPTCHA Bypass] ${new Date().toISOString()}: ${message}`);
|
||||
}
|
||||
|
||||
// Function to get element by selector
|
||||
function qSelector(selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
// Constants
|
||||
const MAX_ATTEMPTS = 1;
|
||||
const SELECTORS = {
|
||||
CHECK_BOX: ".recaptcha-checkbox-border",
|
||||
AUDIO_BUTTON: "#recaptcha-audio-button",
|
||||
PLAY_BUTTON: ".rc-audiochallenge-play-button .rc-button-default",
|
||||
AUDIO_SOURCE: "#audio-source",
|
||||
IMAGE_SELECT: "#rc-imageselect",
|
||||
RESPONSE_FIELD: ".rc-audiochallenge-response-field",
|
||||
AUDIO_ERROR_MESSAGE: ".rc-audiochallenge-error-message",
|
||||
AUDIO_RESPONSE: "#audio-response",
|
||||
RELOAD_BUTTON: "#recaptcha-reload-button",
|
||||
RECAPTCHA_STATUS: "#recaptcha-accessible-status",
|
||||
DOSCAPTCHA: ".rc-doscaptcha-body",
|
||||
VERIFY_BUTTON: "#recaptcha-verify-button"
|
||||
};
|
||||
|
||||
// Function to check if element is hidden
|
||||
function isHidden(el) {
|
||||
return (el.offsetParent === null);
|
||||
}
|
||||
|
||||
// Function to handle the bypass process
|
||||
async function bypassRecaptcha() {
|
||||
try {
|
||||
log('Starting bypass process...');
|
||||
log('Waiting 3 seconds before starting...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
let solved = false;
|
||||
let checkBoxClicked = false;
|
||||
let requestCount = 0;
|
||||
|
||||
const recaptchaInitialStatus = qSelector(SELECTORS.RECAPTCHA_STATUS) ?
|
||||
qSelector(SELECTORS.RECAPTCHA_STATUS).innerText : "";
|
||||
|
||||
log('Initial reCAPTCHA status: ' + recaptchaInitialStatus);
|
||||
|
||||
// Main bypass logic
|
||||
try {
|
||||
if (!checkBoxClicked && qSelector(SELECTORS.CHECK_BOX) &&
|
||||
!isHidden(qSelector(SELECTORS.CHECK_BOX))) {
|
||||
log('Clicking checkbox...');
|
||||
qSelector(SELECTORS.CHECK_BOX).click();
|
||||
checkBoxClicked = true;
|
||||
}
|
||||
|
||||
// Check if the captcha is solved
|
||||
if (qSelector(SELECTORS.RECAPTCHA_STATUS) &&
|
||||
(qSelector(SELECTORS.RECAPTCHA_STATUS).innerText != recaptchaInitialStatus)) {
|
||||
solved = true;
|
||||
log('SOLVED!');
|
||||
}
|
||||
|
||||
if (requestCount > MAX_ATTEMPTS) {
|
||||
log('Attempted Max Retries. Stopping the solver');
|
||||
solved = true;
|
||||
}
|
||||
|
||||
// Stop solving when Automated queries message is shown
|
||||
if (qSelector(SELECTORS.DOSCAPTCHA) &&
|
||||
qSelector(SELECTORS.DOSCAPTCHA).innerText.length > 0) {
|
||||
log('Automated Queries Detected');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
log(`Error in main bypass logic: ${err.message}`);
|
||||
}
|
||||
|
||||
log('Bypass process completed');
|
||||
|
||||
// If not solved, retry after delay
|
||||
if (!solved && requestCount < MAX_ATTEMPTS) {
|
||||
requestCount++;
|
||||
log(`Retrying... Attempt ${requestCount} of ${MAX_ATTEMPTS}`);
|
||||
setTimeout(bypassRecaptcha, 2000);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
log(`Bypass failed: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a MutationObserver to watch for reCAPTCHA elements
|
||||
log('Setting up MutationObserver...');
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType === 1 && // Element node
|
||||
((node.tagName === 'SCRIPT' && node.src && node.src.includes('recaptcha')) ||
|
||||
(node.tagName === 'IFRAME' && node.src && node.src.includes('recaptcha')))) {
|
||||
log(`Detected new reCAPTCHA element: ${node.tagName} - ${node.src}`);
|
||||
bypassRecaptcha();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Start observing
|
||||
observer.observe(document, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
log('MutationObserver started');
|
||||
|
||||
// Run on page load
|
||||
if (document.readyState === 'loading') {
|
||||
log('Document still loading, waiting for DOMContentLoaded');
|
||||
document.addEventListener('DOMContentLoaded', bypassRecaptcha);
|
||||
} else {
|
||||
log('Document already loaded, starting bypass process');
|
||||
bypassRecaptcha();
|
||||
}
|
||||
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
watchdog
|
||||
python-dotenv>=1.0.0
|
||||
colorama>=0.4.6
|
||||
requests
|
||||
psutil>=5.8.0
|
||||
pywin32; platform_system == "Windows"
|
||||
pyinstaller
|
||||
DrissionPage>=4.0.0
|
||||
198
reset_machine_manual.py
Normal file
198
reset_machine_manual.py
Normal file
@@ -0,0 +1,198 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import uuid
|
||||
import hashlib
|
||||
import shutil
|
||||
import sqlite3
|
||||
from logo import print_logo
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# 初始化colorama
|
||||
init()
|
||||
|
||||
# 定义emoji和颜色常量
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
}
|
||||
|
||||
class MachineIDResetter:
|
||||
def __init__(self):
|
||||
|
||||
# 判断操作系统
|
||||
if sys.platform == "win32": # Windows
|
||||
appdata = os.getenv("APPDATA")
|
||||
if appdata is None:
|
||||
raise EnvironmentError("APPDATA Environment Variable Not Set | APPDATA 环境变量未设置")
|
||||
self.db_path = os.path.join(
|
||||
appdata, "Cursor", "User", "globalStorage", "storage.json"
|
||||
)
|
||||
self.sqlite_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/storage.json"
|
||||
))
|
||||
self.sqlite_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/storage.json"
|
||||
))
|
||||
self.sqlite_path = os.path.abspath(os.path.expanduser(
|
||||
"~/.config/Cursor/User/globalStorage/state.vscdb"
|
||||
))
|
||||
else:
|
||||
raise NotImplementedError(f"Not Supported Os| 不支持的操作系统: {sys.platform}")
|
||||
|
||||
def generate_new_ids(self):
|
||||
"""生成新的机器ID"""
|
||||
# 生成新的UUID
|
||||
dev_device_id = str(uuid.uuid4())
|
||||
|
||||
# 生成新的machineId (64个字符的十六进制)
|
||||
machine_id = hashlib.sha256(os.urandom(32)).hexdigest()
|
||||
|
||||
# 生成新的macMachineId (128个字符的十六进制)
|
||||
mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest()
|
||||
|
||||
# 生成新的sqmId
|
||||
sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
|
||||
|
||||
return {
|
||||
"telemetry.devDeviceId": dev_device_id,
|
||||
"telemetry.macMachineId": mac_machine_id,
|
||||
"telemetry.machineId": machine_id,
|
||||
"telemetry.sqmId": sqm_id,
|
||||
"storage.serviceMachineId": dev_device_id, # 添加 storage.serviceMachineId
|
||||
}
|
||||
|
||||
def update_sqlite_db(self, new_ids):
|
||||
"""更新 SQLite 数据库中的机器ID"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Updating SQLite Database | 正在更新 SQLite 数据库...{Style.RESET_ALL}")
|
||||
|
||||
# 创建数据库连接
|
||||
conn = sqlite3.connect(self.sqlite_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 确保表存在
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS ItemTable (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
)
|
||||
""")
|
||||
|
||||
# 准备更新数据
|
||||
updates = [
|
||||
(key, value) for key, value in new_ids.items()
|
||||
]
|
||||
|
||||
# 使用参数化查询来避免 SQL 注入和处理长文本
|
||||
for key, value in updates:
|
||||
cursor.execute("""
|
||||
INSERT OR REPLACE INTO ItemTable (key, value)
|
||||
VALUES (?, ?)
|
||||
""", (key, value))
|
||||
print(f"{EMOJI['INFO']} {Fore.CYAN}Updating Key-Value Pair | 更新键值对: {key}{Style.RESET_ALL}")
|
||||
|
||||
# 提交更改并关闭连接
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} SQLite Database Updated Successfully | 数据库更新成功!{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} SQLite Database Update Failed | 数据库更新失败: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def reset_machine_ids(self):
|
||||
"""重置机器ID并备份原文件"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking Config File | 正在检查配置文件...{Style.RESET_ALL}")
|
||||
|
||||
# 检查JSON文件是否存在
|
||||
if not os.path.exists(self.db_path):
|
||||
print(
|
||||
f"{Fore.RED}{EMOJI['ERROR']} Config File Not Found | 配置文件不存在: {self.db_path}{Style.RESET_ALL}"
|
||||
)
|
||||
return False
|
||||
|
||||
# 检查文件权限
|
||||
if not os.access(self.db_path, os.R_OK | os.W_OK):
|
||||
print(
|
||||
f"{Fore.RED}{EMOJI['ERROR']} Cannot Read or Write Config File, Please Check File Permissions | 无法读写配置文件,请检查文件权限!{Style.RESET_ALL}"
|
||||
)
|
||||
return False
|
||||
|
||||
# 读取现有配置
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} Reading Current Config | 读取当前配置...{Style.RESET_ALL}")
|
||||
with open(self.db_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
|
||||
# 只在没有备份文件时创建备份
|
||||
backup_path = self.db_path + ".bak"
|
||||
if not os.path.exists(backup_path):
|
||||
print(
|
||||
f"{Fore.YELLOW}{EMOJI['BACKUP']} Creating Config Backup | 创建配置备份: {backup_path}{Style.RESET_ALL}"
|
||||
)
|
||||
shutil.copy2(self.db_path, backup_path)
|
||||
else:
|
||||
print(
|
||||
f"{Fore.YELLOW}{EMOJI['INFO']} Backup File Already Exists, Skipping Backup Step | 已存在备份文件,跳过备份步骤{Style.RESET_ALL}"
|
||||
)
|
||||
|
||||
# 生成新的ID
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} Generating New Machine ID | 生成新的机器标识...{Style.RESET_ALL}")
|
||||
new_ids = self.generate_new_ids()
|
||||
|
||||
# 更新配置
|
||||
config.update(new_ids)
|
||||
|
||||
# 保存新配置到 JSON
|
||||
print(f"{Fore.CYAN}{EMOJI['FILE']} Saving New Config to JSON | 保存新配置到 JSON...{Style.RESET_ALL}")
|
||||
with open(self.db_path, "w", encoding="utf-8") as f:
|
||||
json.dump(config, f, indent=4)
|
||||
|
||||
# 更新 SQLite 数据库
|
||||
self.update_sqlite_db(new_ids)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Machine ID Reset Successfully | 机器标识重置成功!{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}New Machine ID | 新的机器标识:{Style.RESET_ALL}")
|
||||
for key, value in new_ids.items():
|
||||
print(f"{EMOJI['INFO']} {key}: {Fore.GREEN}{value}{Style.RESET_ALL}")
|
||||
|
||||
return True
|
||||
|
||||
except PermissionError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Permission Error | 权限错误: {str(e)}{Style.RESET_ALL}")
|
||||
print(
|
||||
f"{Fore.YELLOW}{EMOJI['INFO']} Please Try Running This Program as Administrator | 请尝试以管理员身份运行此程序{Style.RESET_ALL}"
|
||||
)
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Reset Process Error | 重置过程出错: {str(e)}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def run():
|
||||
"""Main function to be called from main.py"""
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} Cursor Machine ID Reset Tool | Cursor 机器标识重置工具{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
resetter = MachineIDResetter()
|
||||
resetter.reset_machine_ids()
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} Press Enter to Exit | 按回车键退出...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -22,11 +22,17 @@ EOF
|
||||
echo -e "${NC}"
|
||||
}
|
||||
|
||||
# 檢查是否為 root 用戶
|
||||
check_root() {
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "${RED}❌ 錯誤: 請使用 sudo 運行此腳本${NC}"
|
||||
exit 1
|
||||
# 获取下载文件夹路径
|
||||
get_downloads_dir() {
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
echo "$HOME/Downloads"
|
||||
else
|
||||
if [ -f "$HOME/.config/user-dirs.dirs" ]; then
|
||||
. "$HOME/.config/user-dirs.dirs"
|
||||
echo "${XDG_DOWNLOAD_DIR:-$HOME/Downloads}"
|
||||
else
|
||||
echo "$HOME/Downloads"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -53,54 +59,29 @@ detect_os() {
|
||||
fi
|
||||
}
|
||||
|
||||
# 創建臨時目錄
|
||||
create_temp_dir() {
|
||||
TMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
}
|
||||
|
||||
# 下載並安裝
|
||||
install_cursor_free_vip() {
|
||||
local install_dir="/usr/local/bin"
|
||||
local downloads_dir=$(get_downloads_dir)
|
||||
local binary_name="CursorFreeVIP_${VERSION}_${OS}"
|
||||
local binary_path="${install_dir}/cursor-free-vip"
|
||||
local binary_path="${downloads_dir}/cursor-free-vip"
|
||||
local download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
|
||||
|
||||
echo -e "${CYAN}ℹ️ 正在下載...${NC}"
|
||||
if ! curl -L -o "${TMP_DIR}/${binary_name}" "$download_url"; then
|
||||
echo -e "${CYAN}ℹ️ 正在下載到 ${downloads_dir}...${NC}"
|
||||
if ! curl -L -o "${binary_path}" "$download_url"; then
|
||||
echo -e "${RED}❌ 下載失敗${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}ℹ️ 正在安裝...${NC}"
|
||||
chmod +x "${TMP_DIR}/${binary_name}"
|
||||
mv "${TMP_DIR}/${binary_name}" "$binary_path"
|
||||
echo -e "${CYAN}ℹ️ 正在設置執行權限...${NC}"
|
||||
chmod +x "${binary_path}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ 安裝完成!${NC}"
|
||||
echo -e "${CYAN}ℹ️ 程序已下載到: ${binary_path}${NC}"
|
||||
echo -e "${CYAN}ℹ️ 正在啟動程序...${NC}"
|
||||
|
||||
# 确保有执行权限
|
||||
chmod +x "$binary_path"
|
||||
|
||||
# 获取实际用户
|
||||
REAL_USER=$SUDO_USER
|
||||
if [ -z "$REAL_USER" ]; then
|
||||
REAL_USER=$(whoami)
|
||||
fi
|
||||
|
||||
# 修改所有权
|
||||
chown $REAL_USER "$binary_path"
|
||||
|
||||
echo -e "${CYAN}ℹ️ 正在以普通用戶身份啟動程序...${NC}"
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
# macOS: 使用 sudo -u 并保持环境变量
|
||||
HOME_DIR=$(eval echo ~$REAL_USER)
|
||||
sudo -u $REAL_USER HOME=$HOME_DIR "$binary_path"
|
||||
else
|
||||
# Linux
|
||||
su - $REAL_USER -c "$binary_path"
|
||||
fi
|
||||
# 直接运行程序
|
||||
"${binary_path}"
|
||||
else
|
||||
echo -e "${RED}❌ 安裝失敗${NC}"
|
||||
exit 1
|
||||
@@ -110,10 +91,8 @@ install_cursor_free_vip() {
|
||||
# 主程序
|
||||
main() {
|
||||
print_logo
|
||||
check_root
|
||||
get_latest_version
|
||||
detect_os
|
||||
create_temp_dir
|
||||
install_cursor_free_vip
|
||||
}
|
||||
|
||||
|
||||
18
turnstilePatch/manifest.json
Normal file
18
turnstilePatch/manifest.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
50
turnstilePatch/recaptcha.js
Normal file
50
turnstilePatch/recaptcha.js
Normal file
@@ -0,0 +1,50 @@
|
||||
function qSelector(selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
var solved = false;
|
||||
var checkBoxClicked = false;
|
||||
var requestCount = 0;
|
||||
const MAX_ATTEMPTS = 1;
|
||||
const CHECK_BOX = ".recaptcha-checkbox-border";
|
||||
const AUDIO_BUTTON = "#recaptcha-audio-button";
|
||||
const PLAY_BUTTON = ".rc-audiochallenge-play-button .rc-button-default";
|
||||
const AUDIO_SOURCE = "#audio-source";
|
||||
const IMAGE_SELECT = "#rc-imageselect";
|
||||
const RESPONSE_FIELD = ".rc-audiochallenge-response-field";
|
||||
const AUDIO_ERROR_MESSAGE = ".rc-audiochallenge-error-message";
|
||||
const AUDIO_RESPONSE = "#audio-response";
|
||||
const RELOAD_BUTTON = "#recaptcha-reload-button";
|
||||
const RECAPTCHA_STATUS = "#recaptcha-accessible-status";
|
||||
const DOSCAPTCHA = ".rc-doscaptcha-body";
|
||||
const VERIFY_BUTTON = "#recaptcha-verify-button";
|
||||
var recaptchaInitialStatus = qSelector(RECAPTCHA_STATUS) ? qSelector(RECAPTCHA_STATUS).innerText : ""
|
||||
function isHidden(el) {
|
||||
return(el.offsetParent === null)
|
||||
}
|
||||
try {
|
||||
if(!checkBoxClicked && qSelector(CHECK_BOX) && !isHidden(qSelector(CHECK_BOX))) {
|
||||
//console.log("checkbox clicked");
|
||||
qSelector(CHECK_BOX).click();
|
||||
checkBoxClicked = true;
|
||||
}
|
||||
//Check if the captcha is solved
|
||||
if(qSelector(RECAPTCHA_STATUS) && (qSelector(RECAPTCHA_STATUS).innerText != recaptchaInitialStatus)) {
|
||||
solved = true;
|
||||
console.log("SOLVED");
|
||||
}
|
||||
if(requestCount > MAX_ATTEMPTS) {
|
||||
console.log("Attempted Max Retries. Stopping the solver");
|
||||
solved = true;
|
||||
}
|
||||
//Stop solving when Automated queries message is shown
|
||||
if(qSelector(DOSCAPTCHA) && qSelector(DOSCAPTCHA).innerText.length > 0) {
|
||||
console.log("Automated Queries Detected");
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err.message);
|
||||
console.log("An error occurred while solving. Stopping the solver.");
|
||||
}
|
||||
})();
|
||||
12
turnstilePatch/script.js
Normal file
12
turnstilePatch/script.js
Normal 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 });
|
||||
69
uBlock0.chromium/1p-filters.html
Normal file
69
uBlock0.chromium/1p-filters.html
Normal file
@@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>uBlock — Your filters</title>
|
||||
|
||||
<link rel="stylesheet" href="lib/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="lib/codemirror/addon/hint/show-hint.css">
|
||||
<link rel="stylesheet" href="lib/codemirror/addon/search/matchesonscrollbar.css">
|
||||
|
||||
<link rel="stylesheet" href="css/themes/default.css">
|
||||
<link rel="stylesheet" href="css/common.css">
|
||||
<link rel="stylesheet" href="css/fa-icons.css">
|
||||
<link rel="stylesheet" href="css/dashboard-common.css">
|
||||
<link rel="stylesheet" href="css/cloud-ui.css">
|
||||
<link rel="stylesheet" href="css/1p-filters.css">
|
||||
<link rel="stylesheet" href="css/codemirror.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="body">
|
||||
<div id="cloudWidget" class="hide" data-cloud-entry="myFiltersPane"></div>
|
||||
<p>
|
||||
<button id="userFiltersApply" class="preferred iconified" type="button" disabled><span class="fa-icon">check</span><span data-i18n="1pApplyChanges">_</span><span class="hover"></span></button>
|
||||
<button id="userFiltersRevert" class="iconified" type="button" disabled><span class="fa-icon">undo</span><span data-i18n="genericRevert">_</span><span class="hover"></span></button>
|
||||
 
|
||||
<button id="importUserFiltersFromFile" class="iconified" type="button"><span class="fa-icon">download-alt</span><span data-i18n="1pImport">_</span><span class="hover"></span></button>
|
||||
<button id="exportUserFiltersToFile" class="iconified" type="button"><span class="fa-icon">upload-alt</span><span data-i18n="1pExport">_</span><span class="hover"></span></button>
|
||||
</p>
|
||||
<p data-i18n="1pTrustWarning"></p>
|
||||
<div class="li"><label><span id="enableMyFilters" class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="1pEnableMyFiltersLabel"></span></label></div>
|
||||
<div id="trustMyFilters" class="li"><label><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="1pTrustMyFiltersLabel"></span></label></div>
|
||||
</div>
|
||||
<div id="userFilters" class="codeMirrorContainer codeMirrorBreakAll cm-theme-override" spellcheck="false"></div>
|
||||
<div class="hidden">
|
||||
<input id="importFilePicker" type="file" accept="text/plain">
|
||||
</div>
|
||||
|
||||
<script src="lib/codemirror/lib/codemirror.js"></script>
|
||||
<script src="lib/codemirror/addon/comment/comment.js"></script>
|
||||
<script src="lib/codemirror/addon/display/panel.js"></script>
|
||||
<script src="lib/codemirror/addon/edit/closebrackets.js"></script>
|
||||
<script src="lib/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="lib/codemirror/addon/fold/foldcode.js"></script>
|
||||
<script src="lib/codemirror/addon/hint/show-hint.js"></script>
|
||||
<script src="lib/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||
<script src="lib/codemirror/addon/search/searchcursor.js"></script>
|
||||
<script src="lib/codemirror/addon/selection/active-line.js"></script>
|
||||
<script src="lib/diff/swatinem_diff.js"></script>
|
||||
<script src="lib/hsluv/hsluv-0.1.0.min.js"></script>
|
||||
|
||||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
|
||||
<script src="js/codemirror/search.js" type="module"></script>
|
||||
<script src="js/codemirror/search-thread.js"></script>
|
||||
|
||||
<script src="js/fa-icons.js" type="module"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/cloud-ui.js" type="module"></script>
|
||||
<script src="js/1p-filters.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
117
uBlock0.chromium/3p-filters.html
Normal file
117
uBlock0.chromium/3p-filters.html
Normal file
@@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
|
||||
<title>uBlock — Filter lists</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/themes/default.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/fa-icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/3p-filters.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="body">
|
||||
|
||||
<div id="cloudWidget" class="hide" data-cloud-entry="tpFiltersPane"></div>
|
||||
<p id="actions">
|
||||
<button id="buttonApply" class="preferred disabled iconified dontshrink" type="button" data-i18n-title="3pApplyChanges"><span class="fa-icon">check</span><span data-i18n="3pApplyChanges">_</span><span class="hover"></span></button>
|
||||
<button id="buttonUpdate" class="preferred disabled iconified" type="button" data-i18n-title="3pUpdateNow"><span class="fa-icon">refresh</span><span data-i18n="3pUpdateNow">_</span><span class="hover"></span></button>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<div class="li">
|
||||
<label><span class="input checkbox"><input type="checkbox" id="autoUpdate"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="3pAutoUpdatePrompt1"></span></label>
|
||||
</div>
|
||||
<div class="li">
|
||||
<label><span class="input checkbox"><input type="checkbox" id="suspendUntilListsAreLoaded"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span data-i18n="3pSuspendUntilListsAreLoaded"></span></label>
|
||||
</div>
|
||||
<div class="li">
|
||||
<label><span class="input checkbox"><input type="checkbox" id="parseCosmeticFilters"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span data-i18n="3pParseAllABPHideFiltersPrompt1"></span> <span class="fa-icon info" data-i18n-title="3pParseAllABPHideFiltersInfo">question-circle</span></span></label>
|
||||
</div>
|
||||
<div class="li">
|
||||
<label><span class="input checkbox"><input type="checkbox" id="ignoreGenericCosmeticFilters"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span data-i18n="3pIgnoreGenericCosmeticFilters"></span> <span class="fa-icon info" data-i18n-title="3pIgnoreGenericCosmeticFiltersInfo">question-circle</span></span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
</div>
|
||||
|
||||
<div id="lists">
|
||||
<div class="rootstats expandable" data-key="*">
|
||||
<span class="fa-icon listExpander">angle-up</span><span id="listsOfBlockedHostsPrompt"></span>
|
||||
</div>
|
||||
<div class="searchfield"><input type="search" spellcheck="false" placeholder="" /><span class="fa-icon">search</span></div>
|
||||
<div class="listEntries"></div>
|
||||
<div class="li listEntry expandable" data-role="import">
|
||||
<span class="detailbar">
|
||||
<label><span class="fa-icon listExpander">angle-up</span><span class="listname" data-i18n="3pImport"></span></label>
|
||||
<a class="fa-icon info towiki" href="https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web" target="_blank">info-circle</a>
|
||||
</span>
|
||||
<textarea dir="ltr" spellcheck="false" placeholder="3pExternalListsHint"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="templates" style="display: none;">
|
||||
<div class="listEntries"></div>
|
||||
<div class="li listEntry" data-role="leaf">
|
||||
<span class="detailbar">
|
||||
<label><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span><span class="listname forinput"></span>
|
||||
</span></label>
|
||||
<span class="iconbar"><!--
|
||||
--><a class="fa-icon content" href="#" type="text/plain" target="_blank" data-i18n-title="3pViewContent">eye-open</a><!--
|
||||
--><a class="fa-icon support" href="#" target="_blank">home</a><!--
|
||||
--><span class="fa-icon remove">trash-o</span><!--
|
||||
--><a class="fa-icon mustread" href="#" target="_blank">info-circle</a><!--
|
||||
--><span class="fa-icon status unsecure" title="http">unlock-alt</span><!--
|
||||
--><span class="fa-icon status obsolete" data-i18n-title="3pExternalListObsolete">exclamation-triangle</span><!--
|
||||
--><span class="fa-icon status cache">clock-o</span><!--
|
||||
--><span class="fa-icon status updating" data-i18n-title="3pUpdating">spinner</span><!--
|
||||
--><span class="fa-icon status failed" data-i18n-title="3pNetworkError">unlink</span>
|
||||
</span>
|
||||
<span class="leafstats"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="li listEntry expandable" data-role="node">
|
||||
<span class="detailbar">
|
||||
<label><span class="input checkbox"><input type="checkbox"><svg viewBox="0 0 24 24"><path d="M1.73,12.91 8.1,19.28 22.79,4.59"/></svg></span><span class="listname forinput"></span></label>
|
||||
<span class="nodestats"></span>
|
||||
<span class="fa-icon listExpander">angle-up</span>
|
||||
<span class="iconbar"><!--
|
||||
--><a class="fa-icon support" href="#" target="_blank">home</a><!--
|
||||
--><a class="fa-icon mustread" href="#" target="_blank">info-circle</a><!--
|
||||
--><span class="fa-icon status obsolete" data-i18n-title="3pExternalListObsolete">exclamation-triangle</span><!--
|
||||
--><span class="fa-icon status cache">clock-o</span><!--
|
||||
--><span class="fa-icon status updating" data-i18n-title="3pUpdating">spinner</span><!--
|
||||
--><span class="fa-icon status failed" data-i18n-title="3pNetworkError">unlink</span>
|
||||
</span>
|
||||
<span class="leafstats"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="li listEntry expandable" data-parent="root" data-role="node">
|
||||
<span class="detailbar">
|
||||
<label><span class="fa-icon listExpander">angle-up</span><span class="listname"></span></label>
|
||||
<span class="nodestats"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="lib/hsluv/hsluv-0.1.0.min.js"></script>
|
||||
|
||||
<script src="js/fa-icons.js" type="module"></script>
|
||||
<script src="js/vapi.js"></script>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/theme.js" type="module"></script>
|
||||
<script src="js/i18n.js" type="module"></script>
|
||||
<script src="js/dashboard-common.js" type="module"></script>
|
||||
<script src="js/cloud-ui.js" type="module"></script>
|
||||
<script src="js/3p-filters.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
674
uBlock0.chromium/LICENSE.txt
Normal file
674
uBlock0.chromium/LICENSE.txt
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. {http://fsf.org/}
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{{project}} Copyright (C) {{year}} {{fullname}}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
{http://www.gnu.org/licenses/}.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
{http://www.gnu.org/philosophy/why-not-lgpl.html}.
|
||||
1314
uBlock0.chromium/_locales/ar/messages.json
Normal file
1314
uBlock0.chromium/_locales/ar/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/az/messages.json
Normal file
1314
uBlock0.chromium/_locales/az/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/be/messages.json
Normal file
1314
uBlock0.chromium/_locales/be/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/bg/messages.json
Normal file
1314
uBlock0.chromium/_locales/bg/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/bn/messages.json
Normal file
1314
uBlock0.chromium/_locales/bn/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/br_FR/messages.json
Normal file
1314
uBlock0.chromium/_locales/br_FR/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/bs/messages.json
Normal file
1314
uBlock0.chromium/_locales/bs/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ca/messages.json
Normal file
1314
uBlock0.chromium/_locales/ca/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/cs/messages.json
Normal file
1314
uBlock0.chromium/_locales/cs/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/cv/messages.json
Normal file
1314
uBlock0.chromium/_locales/cv/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/cy/messages.json
Normal file
1314
uBlock0.chromium/_locales/cy/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/da/messages.json
Normal file
1314
uBlock0.chromium/_locales/da/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/de/messages.json
Normal file
1314
uBlock0.chromium/_locales/de/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/el/messages.json
Normal file
1314
uBlock0.chromium/_locales/el/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1316
uBlock0.chromium/_locales/en/messages.json
Normal file
1316
uBlock0.chromium/_locales/en/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/en_GB/messages.json
Normal file
1314
uBlock0.chromium/_locales/en_GB/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/eo/messages.json
Normal file
1314
uBlock0.chromium/_locales/eo/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/es/messages.json
Normal file
1314
uBlock0.chromium/_locales/es/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/et/messages.json
Normal file
1314
uBlock0.chromium/_locales/et/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/eu/messages.json
Normal file
1314
uBlock0.chromium/_locales/eu/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/fa/messages.json
Normal file
1314
uBlock0.chromium/_locales/fa/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/fi/messages.json
Normal file
1314
uBlock0.chromium/_locales/fi/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/fil/messages.json
Normal file
1314
uBlock0.chromium/_locales/fil/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/fr/messages.json
Normal file
1314
uBlock0.chromium/_locales/fr/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/fy/messages.json
Normal file
1314
uBlock0.chromium/_locales/fy/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/gl/messages.json
Normal file
1314
uBlock0.chromium/_locales/gl/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/gu/messages.json
Normal file
1314
uBlock0.chromium/_locales/gu/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/he/messages.json
Normal file
1314
uBlock0.chromium/_locales/he/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/hi/messages.json
Normal file
1314
uBlock0.chromium/_locales/hi/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/hr/messages.json
Normal file
1314
uBlock0.chromium/_locales/hr/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/hu/messages.json
Normal file
1314
uBlock0.chromium/_locales/hu/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/hy/messages.json
Normal file
1314
uBlock0.chromium/_locales/hy/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/id/messages.json
Normal file
1314
uBlock0.chromium/_locales/id/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/it/messages.json
Normal file
1314
uBlock0.chromium/_locales/it/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ja/messages.json
Normal file
1314
uBlock0.chromium/_locales/ja/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ka/messages.json
Normal file
1314
uBlock0.chromium/_locales/ka/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/kk/messages.json
Normal file
1314
uBlock0.chromium/_locales/kk/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/kn/messages.json
Normal file
1314
uBlock0.chromium/_locales/kn/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ko/messages.json
Normal file
1314
uBlock0.chromium/_locales/ko/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/lt/messages.json
Normal file
1314
uBlock0.chromium/_locales/lt/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/lv/messages.json
Normal file
1314
uBlock0.chromium/_locales/lv/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/mk/messages.json
Normal file
1314
uBlock0.chromium/_locales/mk/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ml/messages.json
Normal file
1314
uBlock0.chromium/_locales/ml/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/mr/messages.json
Normal file
1314
uBlock0.chromium/_locales/mr/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ms/messages.json
Normal file
1314
uBlock0.chromium/_locales/ms/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/nb/messages.json
Normal file
1314
uBlock0.chromium/_locales/nb/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/nl/messages.json
Normal file
1314
uBlock0.chromium/_locales/nl/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/no/messages.json
Normal file
1314
uBlock0.chromium/_locales/no/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/oc/messages.json
Normal file
1314
uBlock0.chromium/_locales/oc/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/pa/messages.json
Normal file
1314
uBlock0.chromium/_locales/pa/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/pl/messages.json
Normal file
1314
uBlock0.chromium/_locales/pl/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/pt_BR/messages.json
Normal file
1314
uBlock0.chromium/_locales/pt_BR/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/pt_PT/messages.json
Normal file
1314
uBlock0.chromium/_locales/pt_PT/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ro/messages.json
Normal file
1314
uBlock0.chromium/_locales/ro/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ru/messages.json
Normal file
1314
uBlock0.chromium/_locales/ru/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/si/messages.json
Normal file
1314
uBlock0.chromium/_locales/si/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/sk/messages.json
Normal file
1314
uBlock0.chromium/_locales/sk/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/sl/messages.json
Normal file
1314
uBlock0.chromium/_locales/sl/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/so/messages.json
Normal file
1314
uBlock0.chromium/_locales/so/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/sq/messages.json
Normal file
1314
uBlock0.chromium/_locales/sq/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/sr/messages.json
Normal file
1314
uBlock0.chromium/_locales/sr/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/sv/messages.json
Normal file
1314
uBlock0.chromium/_locales/sv/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/sw/messages.json
Normal file
1314
uBlock0.chromium/_locales/sw/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ta/messages.json
Normal file
1314
uBlock0.chromium/_locales/ta/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/te/messages.json
Normal file
1314
uBlock0.chromium/_locales/te/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/th/messages.json
Normal file
1314
uBlock0.chromium/_locales/th/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/tr/messages.json
Normal file
1314
uBlock0.chromium/_locales/tr/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/uk/messages.json
Normal file
1314
uBlock0.chromium/_locales/uk/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/ur/messages.json
Normal file
1314
uBlock0.chromium/_locales/ur/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/vi/messages.json
Normal file
1314
uBlock0.chromium/_locales/vi/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/zh_CN/messages.json
Normal file
1314
uBlock0.chromium/_locales/zh_CN/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
1314
uBlock0.chromium/_locales/zh_TW/messages.json
Normal file
1314
uBlock0.chromium/_locales/zh_TW/messages.json
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user