├── requirements.txt ├── img ├── en.png └── tr.jpg ├── README.md ├── README.tr.md ├── .gitignore ├── chrome-extension ├── README.md ├── manifest.json ├── background.js └── content.js ├── start.bat ├── macos_bridge_config.py ├── start-macos.sh ├── windows_bridge_config.py ├── style.qss ├── warp_bridge_server.py ├── warp_proxy_script.py └── languages.py /requirements.txt: -------------------------------------------------------------------------------- 1 | PyQt5 2 | requests 3 | mitmproxy 4 | psutil 5 | -------------------------------------------------------------------------------- /img/en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghondar/warp.dev_account_manager/HEAD/img/en.png -------------------------------------------------------------------------------- /img/tr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghondar/warp.dev_account_manager/HEAD/img/tr.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Warp Account Manager 2 | 3 | ![Warp Account Manager](img/en.png) 4 | 5 | This extension allows you to easily switch between your accounts. 6 | - No ban issues 7 | - Custom chrome extension 8 | 9 | ## NOTE 10 | 11 | This project is designed to facilitate the use of Warp.dev. No responsibility is accepted. 12 | 13 | ## Usage Video: 14 | https://youtu.be/5_itpYHZGJc 15 | -------------------------------------------------------------------------------- /README.tr.md: -------------------------------------------------------------------------------- 1 | # Warp Account Manager 2 | 3 | ![Warp Account Manager](img/tr.jpg) 4 | 5 | Bu eklenti ile hesaplarınız arasında kolayca geçiş yapabilirsiniz. 6 | - Ban problemi yoktur 7 | - Özel chrome eklentisi 8 | 9 | ## NOT 10 | 11 | Bu proje, Warp.dev kullanımını kolaylaştırmak amacıyla hazırlanmıştır. Hiçbir sorumluluk kabul etmez. 12 | 13 | ## Kullanım Videosu: 14 | https://youtu.be/cfcCbqCJUWA 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Database files 2 | accounts.db 3 | 4 | # User settings 5 | user_settings.json 6 | 7 | # Temporary files 8 | account_change_trigger.tmp 9 | 10 | # Python cache 11 | __pycache__/ 12 | *.pyc 13 | *.pyo 14 | *.pyd 15 | 16 | # Virtual environment 17 | venv/ 18 | env/ 19 | .env 20 | 21 | # IDE files 22 | .vscode/ 23 | .idea/ 24 | *.swp 25 | *.swo 26 | 27 | # OS files 28 | .DS_Store 29 | Thumbs.db 30 | -------------------------------------------------------------------------------- /chrome-extension/README.md: -------------------------------------------------------------------------------- 1 | # Warp Account Bridge - Chrome Extension 2 | 3 | Bu Chrome eklentisi Warp hesap verilerini otomatik olarak Python uygulamasına aktarmak için kullanılır. 4 | 5 | ## Kurulum 6 | 7 | 1. Chrome'da `chrome://extensions/` adresine gidin 8 | 2. Sağ üst köşeden "Developer mode" açın 9 | 3. "Load unpacked" butonuna tıklayın 10 | 4. Bu klasörü (`chrome-extension`) seçin 11 | 12 | ## Kullanım 13 | 14 | 1. Python uygulamasını çalıştırın (Bridge server otomatik başlar) 15 | 2. Chrome'da `https://app.warp.dev/logged_in/remote` sayfasına gidin 16 | 3. Sayfada "📡 Add to Warp Manager" butonu görünecek 17 | 4. Butona tıklayarak hesap verilerini otomatik aktarın 18 | 19 | ## Özellikler 20 | 21 | - ✅ Otomatik veri çıkarma 22 | - ✅ Güvenli bridge iletişimi 23 | - ✅ Sabit extension ID 24 | - ✅ Hata yönetimi 25 | - ✅ Görsel geri bildirim 26 | 27 | ## Teknik Detaylar 28 | 29 | - **Port**: 8765 (Python uygulaması) 30 | - **Extension ID**: `warp-account-bridge-v1` 31 | - **Target Page**: `app.warp.dev/logged_in/remote` 32 | - **Data Source**: IndexedDB (firebaseLocalStorageDb) 33 | 34 | ## Dosyalar 35 | 36 | - `manifest.json`: Extension configuration 37 | - `content.js`: Page injection script 38 | - `background.js`: Extension lifecycle management 39 | -------------------------------------------------------------------------------- /chrome-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Warp Account Bridge", 4 | "version": "1.0", 5 | "description": "Bridge extension for Warp account management", 6 | "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXIun41RYc3Ln6RQZI4xLkSzJkAlExAeg3nFLhW5cQNv44OGwglABrBlmcaDxuVQ1bSlm8MITITsUR7g+4rV/d/0BsRLr8EPnZsaek+RMQpMaLUb5O9rBGNXUzh7NhpkBGj3SIpVbj1NA/ruTZP+MoR6SlidjzJQHQXaKAVWdr+pWWJkvTU1I8IysUxKutxcVRNWiwklFQZk/C0mnBPxqvYtYsTXuq44twgIMv4jRydcf0hBAFOKNONEWG8j/hVukAROh6JV0OFNrPzxRp+CuqUynWxN8xYUyPuyzllR5xhpPVRFvgrfDqyr0CPcSuyJA96CeEIQyBnFVD65VjQATQIDAQAB", 7 | "permissions": [ 8 | "tabs", 9 | "storage", 10 | "activeTab", 11 | "scripting" 12 | ], 13 | "host_permissions": [ 14 | "https://app.warp.dev/logged_in/*" 15 | ], 16 | "background": { 17 | "service_worker": "background.js" 18 | }, 19 | "content_scripts": [ 20 | { 21 | "matches": [ 22 | "https://app.warp.dev/logged_in/remote*", 23 | "https://app.warp.dev/logged_in*", 24 | "https://app.warp.dev/login*" 25 | ], 26 | "js": [ 27 | "content.js" 28 | ], 29 | "run_at": "document_end" 30 | } 31 | ], 32 | "action": { 33 | "default_title": "Warp Account Bridge" 34 | }, 35 | "externally_connectable": { 36 | "matches": [ 37 | "http://localhost:*/*" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: Warp Account Manager Launcher - Enhanced Edition 3 | :: Automatic installation and startup script 4 | 5 | title Warp Account Manager - Installation and Startup 6 | chcp 65001 >nul 2>&1 7 | 8 | echo. 9 | echo ==================================================== 10 | echo Warp Account Manager - Automatic Installation 11 | echo ==================================================== 12 | echo. 13 | 14 | :: Administrator permission check 15 | echo [1/6] Checking administrator permissions... 16 | net session >nul 2>&1 17 | if errorlevel 1 ( 18 | echo [ERROR] This application must be run with administrator privileges! 19 | echo. 20 | echo Solution: 21 | echo 1. Right-click on this file 22 | echo 2. Click "Run as administrator" 23 | echo. 24 | pause 25 | exit /b 1 26 | ) 27 | echo [OK] Administrator privileges verified 28 | echo. 29 | 30 | :: Check if Python is installed 31 | echo [2/6] Checking Python installation... 32 | python --version >nul 2>&1 33 | if errorlevel 1 ( 34 | echo [ERROR] Python not found! 35 | echo. 36 | echo Python 3.8 or higher is required. 37 | echo Please download and install Python from https://python.org 38 | echo. 39 | pause 40 | exit /b 1 41 | ) 42 | 43 | for /f "tokens=2" %%i in ('python --version 2^>^&1') do set PYTHON_VERSION=%%i 44 | echo [OK] Python %PYTHON_VERSION% found 45 | echo. 46 | 47 | :: Check if pip is installed 48 | echo [3/6] Checking pip installation... 49 | pip --version >nul 2>&1 50 | if errorlevel 1 ( 51 | echo [ERROR] pip not found! 52 | echo. 53 | echo pip should come with Python. 54 | echo Try reinstalling Python. 55 | echo. 56 | pause 57 | exit /b 1 58 | ) 59 | echo [OK] pip found 60 | echo. 61 | 62 | :: Check and install required packages 63 | echo [4/6] Checking required Python packages... 64 | echo. 65 | 66 | :: Package list 67 | set PACKAGES=PyQt5 requests mitmproxy psutil 68 | 69 | :: Check each package 70 | for %%p in (%PACKAGES%) do ( 71 | echo Checking: %%p 72 | pip show %%p >nul 2>&1 73 | if errorlevel 1 ( 74 | echo [MISSING] Installing %%p... 75 | pip install %%p 76 | if errorlevel 1 ( 77 | echo [ERROR] Failed to install %%p! 78 | echo. 79 | echo Please check your internet connection and try again. 80 | echo. 81 | pause 82 | exit /b 1 83 | ) 84 | echo [OK] %%p successfully installed 85 | ) else ( 86 | echo [OK] %%p already installed 87 | ) 88 | ) 89 | 90 | echo. 91 | echo [OK] All required packages are ready 92 | echo. 93 | 94 | :: Database file check 95 | echo [5/6] Checking database file... 96 | if exist "accounts.db" ( 97 | echo [OK] Database file exists 98 | ) else ( 99 | echo [INFO] Database file will be created 100 | ) 101 | echo. 102 | 103 | :: Start Warp Account Manager 104 | echo [6/6] Starting Warp Account Manager... 105 | echo. 106 | echo ==================================================== 107 | echo Installation completed - Starting application 108 | echo ==================================================== 109 | echo. 110 | 111 | :: Navigate to script directory 112 | cd /d "%~dp0" 113 | 114 | if exist "warp_account_manager.py" ( 115 | echo Opening Warp Account Manager... 116 | echo. 117 | echo NOTE: Do not close this window! This console window 118 | echo must remain open while the application is running. 119 | echo. 120 | python warp_account_manager.py 121 | 122 | echo. 123 | echo Warp Account Manager closed. 124 | ) else ( 125 | echo [ERROR] warp_account_manager.py file not found! 126 | echo. 127 | echo Current directory: %CD% 128 | echo Script directory: %~dp0 129 | echo. 130 | echo Please ensure all files are in the correct location. 131 | ) 132 | 133 | echo. 134 | echo Press any key to exit... 135 | pause >nul 136 | -------------------------------------------------------------------------------- /chrome-extension/background.js: -------------------------------------------------------------------------------- 1 | // Background script for Warp Account Bridge 2 | // Handles extension lifecycle and bridge communication 3 | 4 | const BRIDGE_CONFIG = { 5 | pythonAppPort: 8765, 6 | extensionId: "warp-account-bridge-v1", 7 | }; 8 | 9 | // Check if Python app is running 10 | async function checkPythonApp() { 11 | try { 12 | const response = await fetch(`http://localhost:${BRIDGE_CONFIG.pythonAppPort}/health`, { 13 | method: "GET", 14 | headers: { 15 | "X-Extension-ID": BRIDGE_CONFIG.extensionId, 16 | }, 17 | }); 18 | return response.ok; 19 | } catch (error) { 20 | return false; 21 | } 22 | } 23 | 24 | // Setup bridge configuration on Python app 25 | async function setupBridge() { 26 | try { 27 | const response = await fetch(`http://localhost:${BRIDGE_CONFIG.pythonAppPort}/setup-bridge`, { 28 | method: "POST", 29 | headers: { 30 | "Content-Type": "application/json", 31 | "X-Extension-ID": BRIDGE_CONFIG.extensionId, 32 | }, 33 | body: JSON.stringify({ 34 | extensionId: chrome.runtime.id, 35 | version: chrome.runtime.getManifest().version, 36 | }), 37 | }); 38 | 39 | if (response.ok) { 40 | console.log("Bridge setup successful"); 41 | return true; 42 | } else { 43 | console.log("Bridge setup failed:", await response.text()); 44 | return false; 45 | } 46 | } catch (error) { 47 | console.log("Bridge setup error:", error); 48 | return false; 49 | } 50 | } 51 | 52 | // Extension installed/startup 53 | chrome.runtime.onInstalled.addListener(async (details) => { 54 | console.log("Warp Account Bridge installed/updated"); 55 | 56 | // Try to setup bridge with Python app 57 | setTimeout(async () => { 58 | const isAppRunning = await checkPythonApp(); 59 | if (isAppRunning) { 60 | await setupBridge(); 61 | } 62 | }, 2000); 63 | }); 64 | 65 | // Extension startup 66 | chrome.runtime.onStartup.addListener(async () => { 67 | console.log("Warp Account Bridge started"); 68 | 69 | // Try to setup bridge with Python app 70 | setTimeout(async () => { 71 | const isAppRunning = await checkPythonApp(); 72 | if (isAppRunning) { 73 | await setupBridge(); 74 | } 75 | }, 2000); 76 | }); 77 | 78 | // Handle messages from content scripts 79 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 80 | if (request.type === "EXTRACT_ACCOUNT_DATA") { 81 | // This could be used for alternative data extraction if needed 82 | handleAccountDataExtraction(request, sender, sendResponse); 83 | return true; // Will respond asynchronously 84 | } else if (request.type === "CHECK_PYTHON_APP") { 85 | checkPythonApp().then(sendResponse); 86 | return true; 87 | } 88 | }); 89 | 90 | // Handle account data extraction (alternative method) 91 | async function handleAccountDataExtraction(request, sender, sendResponse) { 92 | try { 93 | // Execute script in the content script's context to extract data 94 | const results = await chrome.scripting.executeScript({ 95 | target: { tabId: sender.tab.id }, 96 | func: extractFirebaseData, 97 | }); 98 | 99 | if (results && results[0] && results[0].result) { 100 | sendResponse({ success: true, data: results[0].result }); 101 | } else { 102 | sendResponse({ success: false, error: "No data found" }); 103 | } 104 | } catch (error) { 105 | sendResponse({ success: false, error: error.message }); 106 | } 107 | } 108 | 109 | // Function to inject into page for data extraction 110 | function extractFirebaseData() { 111 | return new Promise((resolve, reject) => { 112 | try { 113 | const request = indexedDB.open("firebaseLocalStorageDb"); 114 | 115 | request.onsuccess = function (event) { 116 | const db = event.target.result; 117 | const transaction = db.transaction(["firebaseLocalStorage"], "readonly"); 118 | const objectStore = transaction.objectStore("firebaseLocalStorage"); 119 | 120 | objectStore.getAll().onsuccess = function (event) { 121 | const results = event.target.result; 122 | 123 | for (let result of results) { 124 | if (result.value && typeof result.value === "object" && result.value.email && result.value.stsTokenManager) { 125 | resolve(result.value); 126 | return; 127 | } 128 | } 129 | reject(new Error("No valid account data found")); 130 | }; 131 | 132 | objectStore.getAll().onerror = () => reject(new Error("Database read error")); 133 | }; 134 | 135 | request.onerror = () => reject(new Error("Database connection error")); 136 | } catch (error) { 137 | reject(error); 138 | } 139 | }); 140 | } 141 | 142 | // Monitor tab changes to detect warp.dev visits 143 | chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { 144 | if (changeInfo.status === "complete" && tab.url && tab.url.includes("app.warp.dev/logged_in")) { 145 | // Check if Python app is running when user visits the target page 146 | const isAppRunning = await checkPythonApp(); 147 | if (!isAppRunning) { 148 | // Could show a notification here if needed 149 | console.log("Python app not running when visiting Warp page"); 150 | } 151 | } 152 | }); 153 | 154 | // Periodic health check (every 5 minutes) 155 | setInterval(async () => { 156 | const isAppRunning = await checkPythonApp(); 157 | if (isAppRunning) { 158 | // Ensure bridge is properly configured 159 | await setupBridge(); 160 | } 161 | }, 300000); // 5 minutes 162 | -------------------------------------------------------------------------------- /macos_bridge_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | macOS Bridge Configuration - Native messaging setup for Chrome extension 6 | """ 7 | 8 | import os 9 | import sys 10 | import json 11 | from pathlib import Path 12 | 13 | 14 | class MacOSBridgeConfig: 15 | def __init__(self): 16 | self.extension_id = "warp-account-bridge-v1" 17 | self.app_name = "com.warp.account.bridge" 18 | self.native_messaging_dir = Path.home() / "Library/Application Support/Google/Chrome/NativeMessagingHosts" 19 | 20 | def is_admin(self): 21 | """Check if running as administrator (root)""" 22 | return os.getuid() == 0 23 | 24 | def setup_localhost_access(self): 25 | """Configure macOS for localhost access from extensions""" 26 | try: 27 | print("🔧 Chrome extension manifest localhost access...") 28 | 29 | # Chrome extension uses externally_connectable in manifest 30 | # No additional macOS-specific registry settings needed 31 | print("✅ Manifest-based localhost access active") 32 | print("📋 Extension manifest has externally_connectable configuration") 33 | 34 | return True 35 | 36 | except Exception as e: 37 | print(f"❌ Localhost access setup error: {e}") 38 | return False 39 | 40 | def create_native_messaging_manifest(self): 41 | """Create native messaging host manifest for macOS""" 42 | try: 43 | # Python executable path 44 | python_exe = sys.executable 45 | script_path = os.path.abspath("warp_account_manager.py") 46 | 47 | manifest = { 48 | "name": self.app_name, 49 | "description": "Warp Account Bridge Native Host", 50 | "path": python_exe, 51 | "type": "stdio", 52 | "allowed_origins": [ 53 | f"chrome-extension://{self.extension_id}/" 54 | ] 55 | } 56 | 57 | # Create manifest directory if it doesn't exist 58 | self.native_messaging_dir.mkdir(parents=True, exist_ok=True) 59 | 60 | manifest_path = self.native_messaging_dir / f"{self.app_name}.json" 61 | with open(manifest_path, 'w') as f: 62 | json.dump(manifest, f, indent=2) 63 | 64 | print(f"✅ Native messaging manifest created: {manifest_path}") 65 | return str(manifest_path) 66 | 67 | except Exception as e: 68 | print(f"❌ Manifest creation error: {e}") 69 | return None 70 | 71 | def register_native_host(self): 72 | """Register native messaging host (macOS uses file-based registration)""" 73 | try: 74 | manifest_path = self.create_native_messaging_manifest() 75 | if not manifest_path: 76 | return False 77 | 78 | print(f"✅ Native host registered: {manifest_path}") 79 | return True 80 | 81 | except Exception as e: 82 | print(f"❌ Native host registration error: {e}") 83 | return False 84 | 85 | def setup_bridge_config(self): 86 | """Complete bridge configuration for macOS""" 87 | print("🌉 macOS Bridge configuration starting...") 88 | 89 | # 1. Localhost access setup 90 | localhost_ok = self.setup_localhost_access() 91 | 92 | # 2. Native messaging host registration 93 | native_ok = self.register_native_host() 94 | 95 | if localhost_ok and native_ok: 96 | print("✅ Bridge configuration completed!") 97 | print("\n📋 Next steps:") 98 | print("1. Restart Chrome") 99 | print("2. Load extension from chrome://extensions/ page") 100 | print("3. Start Warp Account Manager") 101 | return True 102 | else: 103 | print("❌ Bridge configuration failed!") 104 | return False 105 | 106 | def check_configuration(self): 107 | """Check if bridge is properly configured""" 108 | try: 109 | print("🔍 Checking bridge configuration...") 110 | 111 | # Check if manifest file exists 112 | manifest_path = self.native_messaging_dir / f"{self.app_name}.json" 113 | if manifest_path.exists(): 114 | print("✅ Native messaging manifest found") 115 | return True 116 | else: 117 | print("❌ Native messaging manifest not found") 118 | return False 119 | 120 | except Exception as e: 121 | print(f"❌ Configuration check error: {e}") 122 | return False 123 | 124 | def remove_configuration(self): 125 | """Remove bridge configuration (cleanup)""" 126 | try: 127 | print("🧹 Cleaning up bridge configuration...") 128 | 129 | # Remove manifest file 130 | manifest_path = self.native_messaging_dir / f"{self.app_name}.json" 131 | if manifest_path.exists(): 132 | manifest_path.unlink() 133 | print("✅ Manifest file removed") 134 | else: 135 | print("⚠️ Manifest file not found") 136 | 137 | return True 138 | 139 | except Exception as e: 140 | print(f"❌ Cleanup error: {e}") 141 | return False 142 | 143 | 144 | def setup_bridge(): 145 | """Setup bridge configuration""" 146 | config = MacOSBridgeConfig() 147 | return config.setup_bridge_config() 148 | 149 | def check_bridge(): 150 | """Check bridge configuration""" 151 | config = MacOSBridgeConfig() 152 | return config.check_configuration() 153 | 154 | def remove_bridge(): 155 | """Remove bridge configuration""" 156 | config = MacOSBridgeConfig() 157 | return config.remove_configuration() 158 | 159 | 160 | if __name__ == "__main__": 161 | if len(sys.argv) > 1: 162 | action = sys.argv[1] 163 | 164 | if action == "setup": 165 | setup_bridge() 166 | elif action == "check": 167 | check_bridge() 168 | elif action == "remove": 169 | remove_bridge() 170 | else: 171 | print("Usage: python macos_bridge_config.py [setup|check|remove]") 172 | else: 173 | # Default: setup 174 | setup_bridge() -------------------------------------------------------------------------------- /start-macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Warp Account Manager Launcher - macOS Edition 3 | # Automatic installation and startup script 4 | 5 | # Colors for better output 6 | RED='\033[0;31m' 7 | GREEN='\033[0;32m' 8 | YELLOW='\033[1;33m' 9 | BLUE='\033[0;34m' 10 | NC='\033[0m' # No Color 11 | 12 | # Clear screen and show header 13 | clear 14 | echo 15 | echo "====================================================" 16 | echo " Warp Account Manager - Automatic Installation" 17 | echo "====================================================" 18 | echo 19 | 20 | # Function to print status messages 21 | print_status() { 22 | echo -e "${GREEN}[OK]${NC} $1" 23 | } 24 | 25 | print_info() { 26 | echo -e "${BLUE}[INFO]${NC} $1" 27 | } 28 | 29 | print_error() { 30 | echo -e "${RED}[ERROR]${NC} $1" 31 | } 32 | 33 | print_warning() { 34 | echo -e "${YELLOW}[WARNING]${NC} $1" 35 | } 36 | 37 | # Navigate to script directory 38 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 39 | cd "$SCRIPT_DIR" 40 | 41 | # Check if running as root (administrator equivalent) 42 | echo "[1/6] Checking permissions..." 43 | if [[ $EUID -eq 0 ]]; then 44 | print_warning "Running as root - this is not recommended for this application" 45 | print_info "You may run this script as a regular user" 46 | else 47 | print_status "Running as regular user" 48 | fi 49 | echo 50 | 51 | # Check if Python is installed 52 | echo "[2/6] Checking Python installation..." 53 | if command -v python3 &> /dev/null; then 54 | PYTHON_VERSION=$(python3 --version 2>&1 | cut -d' ' -f2) 55 | print_status "Python $PYTHON_VERSION found" 56 | PYTHON_CMD="python3" 57 | elif command -v python &> /dev/null; then 58 | PYTHON_VERSION=$(python --version 2>&1 | cut -d' ' -f2) 59 | # Check if it's Python 3 60 | if python -c "import sys; exit(0 if sys.version_info[0] >= 3 else 1)" 2>/dev/null; then 61 | print_status "Python $PYTHON_VERSION found" 62 | PYTHON_CMD="python" 63 | else 64 | print_error "Python 3.8 or higher is required, but found Python 2" 65 | echo 66 | echo "Please install Python 3 using one of these methods:" 67 | echo "1. Homebrew: brew install python3" 68 | echo "2. Python.org: https://python.org" 69 | echo "3. Pyenv: pyenv install 3.11" 70 | echo 71 | read -p "Press Enter to exit..." 72 | exit 1 73 | fi 74 | else 75 | print_error "Python not found!" 76 | echo 77 | echo "Python 3.8 or higher is required." 78 | echo "Please install Python using one of these methods:" 79 | echo "1. Homebrew: brew install python3" 80 | echo "2. Python.org: https://python.org" 81 | echo "3. Pyenv: pyenv install 3.11" 82 | echo 83 | read -p "Press Enter to exit..." 84 | exit 1 85 | fi 86 | echo 87 | 88 | # Check if pip is installed 89 | echo "[3/6] Checking pip installation..." 90 | if command -v pip3 &> /dev/null; then 91 | print_status "pip3 found" 92 | PIP_CMD="pip3" 93 | elif command -v pip &> /dev/null; then 94 | print_status "pip found" 95 | PIP_CMD="pip" 96 | else 97 | print_error "pip not found!" 98 | echo 99 | echo "pip should come with Python." 100 | echo "Try reinstalling Python or install pip manually:" 101 | echo "curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py" 102 | echo "$PYTHON_CMD get-pip.py" 103 | echo 104 | read -p "Press Enter to exit..." 105 | exit 1 106 | fi 107 | echo 108 | 109 | # Check and install required packages 110 | echo "[4/6] Checking required Python packages..." 111 | echo 112 | 113 | # Package list 114 | PACKAGES=("PyQt5" "requests" "mitmproxy" "psutil") 115 | 116 | # Check each package 117 | for package in "${PACKAGES[@]}"; do 118 | echo " Checking: $package" 119 | if $PIP_CMD show "$package" > /dev/null 2>&1; then 120 | print_status " $package already installed" 121 | else 122 | print_info " [MISSING] Installing $package..." 123 | 124 | # Try different installation methods for macOS 125 | if [[ "$package" == "PyQt5" ]]; then 126 | # PyQt5 via Homebrew on macOS 127 | if command -v brew &> /dev/null; then 128 | echo " Trying Homebrew installation for PyQt5..." 129 | if brew install pyqt@5; then 130 | print_status " $package successfully installed via Homebrew" 131 | continue 132 | fi 133 | fi 134 | elif [[ "$package" == "mitmproxy" ]]; then 135 | # mitmproxy via Homebrew (already should be installed) 136 | if command -v mitmdump &> /dev/null; then 137 | print_status " mitmproxy already available via Homebrew" 138 | continue 139 | fi 140 | fi 141 | 142 | # Try pip with --break-system-packages as fallback 143 | if $PIP_CMD install "$package" --break-system-packages; then 144 | print_status " $package successfully installed" 145 | else 146 | print_error " Failed to install $package!" 147 | echo 148 | echo " Possible solutions:" 149 | echo " 1. Install via Homebrew: brew install $package" 150 | echo " 2. Create virtual environment" 151 | echo " 3. Try: $PIP_CMD install --user $package --break-system-packages" 152 | echo 153 | read -p "Continue anyway? (y/N): " -n 1 -r 154 | echo 155 | if [[ ! $REPLY =~ ^[Yy]$ ]]; then 156 | exit 1 157 | fi 158 | fi 159 | fi 160 | done 161 | 162 | echo 163 | print_status "All required packages are ready" 164 | echo 165 | 166 | # Database file check 167 | echo "[5/6] Checking database file..." 168 | if [[ -f "accounts.db" ]]; then 169 | print_status "Database file exists" 170 | else 171 | print_info "Database file will be created" 172 | fi 173 | echo 174 | 175 | # Start Warp Account Manager 176 | echo "[6/6] Starting Warp Account Manager..." 177 | echo 178 | echo "====================================================" 179 | echo " Installation completed - Starting application" 180 | echo "====================================================" 181 | echo 182 | 183 | if [[ -f "warp_account_manager.py" ]]; then 184 | print_info "Opening Warp Account Manager..." 185 | echo 186 | print_warning "NOTE: Do not close this terminal window! This console window" 187 | print_warning " must remain open while the application is running." 188 | echo 189 | 190 | # Start the application 191 | $PYTHON_CMD warp_account_manager.py 192 | 193 | echo 194 | print_info "Warp Account Manager closed." 195 | else 196 | print_error "warp_account_manager.py file not found!" 197 | echo 198 | echo "Current directory: $(pwd)" 199 | echo "Script directory: $SCRIPT_DIR" 200 | echo 201 | echo "Please ensure all files are in the correct location." 202 | fi 203 | 204 | echo 205 | echo "Press Enter to exit..." 206 | read -------------------------------------------------------------------------------- /windows_bridge_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Windows Bridge Configuration - Registry settings for Chrome extension 6 | """ 7 | 8 | import winreg 9 | import os 10 | import sys 11 | import json 12 | from pathlib import Path 13 | 14 | class WindowsBridgeConfig: 15 | def __init__(self): 16 | self.extension_id = "warp-account-bridge-v1" 17 | self.app_name = "com.warp.account.bridge" 18 | self.registry_paths = [ 19 | r"SOFTWARE\Google\Chrome\NativeMessagingHosts", 20 | r"SOFTWARE\Microsoft\Edge\NativeMessagingHosts" 21 | ] 22 | 23 | def is_admin(self): 24 | """Check if running as administrator""" 25 | try: 26 | return os.getuid() == 0 27 | except AttributeError: 28 | # Windows 29 | import ctypes 30 | try: 31 | return ctypes.windll.shell32.IsUserAnAdmin() 32 | except: 33 | return False 34 | 35 | def setup_localhost_access(self): 36 | """Configure Windows for localhost access from extensions""" 37 | try: 38 | print("🔧 Chrome extension manifest ile localhost erişimi...") 39 | 40 | # Chrome extension manifest'te externally_connectable kullanıyoruz 41 | # Registry ayarı gerekmez, manifest yeterli 42 | print("✅ Manifest-based localhost erişimi aktif") 43 | print("📋 Extension manifest'te externally_connectable yapılandırması mevcut") 44 | 45 | return True 46 | 47 | except Exception as e: 48 | print(f"❌ Localhost erişim ayarı hatası: {e}") 49 | return False 50 | 51 | def create_native_messaging_manifest(self): 52 | """Create native messaging host manifest""" 53 | try: 54 | # Python executable path 55 | python_exe = sys.executable 56 | script_path = os.path.abspath("warp_account_manager.py") 57 | 58 | manifest = { 59 | "name": self.app_name, 60 | "description": "Warp Account Bridge Native Host", 61 | "path": python_exe, 62 | "type": "stdio", 63 | "allowed_origins": [ 64 | f"chrome-extension://{self.extension_id}/" 65 | ] 66 | } 67 | 68 | # Manifest dosyasını kaydet 69 | manifest_dir = os.path.join(os.getenv('APPDATA'), 'WarpAccountManager') 70 | os.makedirs(manifest_dir, exist_ok=True) 71 | 72 | manifest_path = os.path.join(manifest_dir, f"{self.app_name}.json") 73 | with open(manifest_path, 'w') as f: 74 | json.dump(manifest, f, indent=2) 75 | 76 | print(f"✅ Native messaging manifest oluşturuldu: {manifest_path}") 77 | return manifest_path 78 | 79 | except Exception as e: 80 | print(f"❌ Manifest oluşturma hatası: {e}") 81 | return None 82 | 83 | def register_native_host(self): 84 | """Register native messaging host in registry""" 85 | try: 86 | manifest_path = self.create_native_messaging_manifest() 87 | if not manifest_path: 88 | return False 89 | 90 | success = False 91 | 92 | for registry_path in self.registry_paths: 93 | try: 94 | # HKEY_CURRENT_USER'da kaydet (yönetici gerektirmez) 95 | key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, registry_path) 96 | winreg.SetValueEx(key, self.app_name, 0, winreg.REG_SZ, manifest_path) 97 | winreg.CloseKey(key) 98 | print(f"✅ Native host kaydedildi: {registry_path}") 99 | success = True 100 | 101 | except Exception as e: 102 | print(f"⚠️ Registry kaydı hatası ({registry_path}): {e}") 103 | 104 | return success 105 | 106 | except Exception as e: 107 | print(f"❌ Native host kayıt hatası: {e}") 108 | return False 109 | 110 | def setup_bridge_config(self): 111 | """Complete bridge configuration""" 112 | print("🌉 Windows Bridge konfigürasyonu başlatılıyor...") 113 | 114 | # 1. Localhost erişim ayarları 115 | localhost_ok = self.setup_localhost_access() 116 | 117 | # 2. Native messaging host kaydı (opsiyonel) 118 | # native_ok = self.register_native_host() 119 | 120 | if localhost_ok: 121 | print("✅ Bridge konfigürasyonu tamamlandı!") 122 | print("\n📋 Sonraki adımlar:") 123 | print("1. Chrome'u yeniden başlat") 124 | print("2. Eklentiyi chrome://extensions/ sayfasından yükle") 125 | print("3. Warp Account Manager'ı başlat") 126 | return True 127 | else: 128 | print("❌ Bridge konfigürasyonu başarısız!") 129 | return False 130 | 131 | def check_configuration(self): 132 | """Check if bridge is properly configured""" 133 | try: 134 | print("🔍 Bridge konfigürasyon kontrol ediliyor...") 135 | 136 | # Manifest-based konfigürasyon için her zaman True döndür 137 | # Gerçek kontrol extension yüklendiğinde yapılacak 138 | print("✅ Manifest-based bridge konfigürasyonu") 139 | return True 140 | 141 | except Exception as e: 142 | print(f"❌ Konfigürasyon kontrol hatası: {e}") 143 | return False 144 | 145 | def remove_configuration(self): 146 | """Remove bridge configuration (cleanup)""" 147 | try: 148 | print("🧹 Bridge konfigürasyonu temizleniyor...") 149 | 150 | # Registry temizliği 151 | chrome_policies_path = r"SOFTWARE\Policies\Google\Chrome" 152 | 153 | try: 154 | key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, chrome_policies_path, 0, winreg.KEY_SET_VALUE) 155 | winreg.DeleteValue(key, "URLAllowlist") 156 | winreg.CloseKey(key) 157 | print("✅ Chrome policy temizlendi") 158 | except FileNotFoundError: 159 | print("⚠️ Chrome policy zaten mevcut değil") 160 | 161 | # Manifest dosyası temizliği 162 | manifest_dir = os.path.join(os.getenv('APPDATA'), 'WarpAccountManager') 163 | manifest_path = os.path.join(manifest_dir, f"{self.app_name}.json") 164 | 165 | if os.path.exists(manifest_path): 166 | os.remove(manifest_path) 167 | print("✅ Manifest dosyası silindi") 168 | 169 | return True 170 | 171 | except Exception as e: 172 | print(f"❌ Temizlik hatası: {e}") 173 | return False 174 | 175 | 176 | def setup_bridge(): 177 | """Setup bridge configuration""" 178 | config = WindowsBridgeConfig() 179 | return config.setup_bridge_config() 180 | 181 | def check_bridge(): 182 | """Check bridge configuration""" 183 | config = WindowsBridgeConfig() 184 | return config.check_configuration() 185 | 186 | def remove_bridge(): 187 | """Remove bridge configuration""" 188 | config = WindowsBridgeConfig() 189 | return config.remove_configuration() 190 | 191 | 192 | if __name__ == "__main__": 193 | if len(sys.argv) > 1: 194 | action = sys.argv[1] 195 | 196 | if action == "setup": 197 | setup_bridge() 198 | elif action == "check": 199 | check_bridge() 200 | elif action == "remove": 201 | remove_bridge() 202 | else: 203 | print("Kullanım: python windows_bridge_config.py [setup|check|remove]") 204 | else: 205 | # Varsayılan: setup 206 | setup_bridge() 207 | -------------------------------------------------------------------------------- /style.qss: -------------------------------------------------------------------------------- 1 | /* Modern Compact Theme for Warp Hesap Yöneticisi */ 2 | 3 | /* Global */ 4 | QMainWindow, QWidget { 5 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 6 | stop: 0 #f8fafc, stop: 1 #f1f5f9); 7 | color: #1e293b; 8 | font-family: "Segoe UI", "SF Pro Display", "Inter", system-ui, sans-serif; 9 | font-size: 10pt; 10 | font-weight: 400; 11 | } 12 | 13 | /* Labels */ 14 | QLabel { 15 | color: #334155; 16 | font-weight: 500; 17 | } 18 | 19 | /* Status Bar */ 20 | QStatusBar { 21 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 22 | stop: 0 #ffffff, stop: 1 #f8fafc); 23 | border-top: 1px solid #e2e8f0; 24 | color: #64748b; 25 | font-size: 9pt; 26 | } 27 | 28 | /* Buttons - Modern Glass Effect */ 29 | QPushButton { 30 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 31 | stop: 0 #ffffff, stop: 1 #f8fafc); 32 | border: 1px solid #e2e8f0; 33 | border-radius: 8px; 34 | padding: 8px 16px; 35 | color: #475569; 36 | font-weight: 500; 37 | font-size: 10pt; 38 | } 39 | 40 | QPushButton:hover { 41 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 42 | stop: 0 #f8fafc, stop: 1 #f1f5f9); 43 | border-color: #cbd5e1; 44 | } 45 | 46 | QPushButton:pressed { 47 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 48 | stop: 0 #e2e8f0, stop: 1 #cbd5e1); 49 | } 50 | 51 | QPushButton:disabled { 52 | color: #94a3b8; 53 | background: #f8fafc; 54 | border-color: #f1f5f9; 55 | } 56 | 57 | /* Accent Buttons with Beautiful Gradients */ 58 | QPushButton#StartButton { 59 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 60 | stop: 0 #10b981, stop: 1 #059669); 61 | border: 1px solid #047857; 62 | color: white; 63 | font-weight: 600; 64 | } 65 | QPushButton#StartButton:hover { 66 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 67 | stop: 0 #34d399, stop: 1 #10b981); 68 | border-color: #065f46; 69 | } 70 | 71 | QPushButton#StopButton { 72 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 73 | stop: 0 #ef4444, stop: 1 #dc2626); 74 | border: 1px solid #b91c1c; 75 | color: white; 76 | font-weight: 600; 77 | } 78 | QPushButton#StopButton:hover { 79 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 80 | stop: 0 #f87171, stop: 1 #ef4444); 81 | border-color: #991b1b; 82 | } 83 | 84 | QPushButton#AddButton { 85 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 86 | stop: 0 #3b82f6, stop: 1 #2563eb); 87 | border: 1px solid #1d4ed8; 88 | color: white; 89 | font-weight: 600; 90 | } 91 | QPushButton#AddButton:hover { 92 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 93 | stop: 0 #60a5fa, stop: 1 #3b82f6); 94 | border-color: #1e40af; 95 | } 96 | 97 | QPushButton#RefreshButton { 98 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 99 | stop: 0 #8b5cf6, stop: 1 #7c3aed); 100 | border: 1px solid #6d28d9; 101 | color: white; 102 | font-weight: 600; 103 | } 104 | QPushButton#RefreshButton:hover { 105 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 106 | stop: 0 #a78bfa, stop: 1 #8b5cf6); 107 | border-color: #5b21b6; 108 | } 109 | 110 | /* ComboBox - Modern Style */ 111 | QComboBox { 112 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 113 | stop: 0 #ffffff, stop: 1 #f8fafc); 114 | border: 1px solid #e2e8f0; 115 | border-radius: 6px; 116 | padding: 4px 8px; 117 | color: #475569; 118 | font-weight: 500; 119 | } 120 | 121 | QComboBox:hover { 122 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 123 | stop: 0 #f8fafc, stop: 1 #f1f5f9); 124 | border-color: #cbd5e1; 125 | } 126 | 127 | QComboBox::drop-down { 128 | border: none; 129 | width: 20px; 130 | } 131 | 132 | QComboBox::down-arrow { 133 | width: 12px; 134 | height: 12px; 135 | margin-right: 4px; 136 | } 137 | 138 | QComboBox QAbstractItemView { 139 | background: #ffffff; 140 | border: 1px solid #e2e8f0; 141 | border-radius: 8px; 142 | selection-background-color: #3b82f6; 143 | selection-color: white; 144 | outline: none; 145 | } 146 | 147 | /* Text Edits - Clean and Modern */ 148 | QTextEdit { 149 | background: #ffffff; 150 | border: 2px solid #e2e8f0; 151 | border-radius: 10px; 152 | padding: 12px; 153 | color: #334155; 154 | font-size: 10pt; 155 | selection-background-color: #dbeafe; 156 | } 157 | 158 | QTextEdit:focus { 159 | border-color: #3b82f6; 160 | background: #fefefe; 161 | } 162 | 163 | /* Table - Beautiful Modern Design */ 164 | QTableWidget { 165 | background: #ffffff; 166 | border: 1px solid #e2e8f0; 167 | border-radius: 12px; 168 | gridline-color: transparent; 169 | alternate-background-color: #f8fafc; 170 | selection-background-color: #dbeafe; 171 | selection-color: #1e293b; 172 | outline: none; 173 | } 174 | 175 | QTableWidget::item { 176 | padding: 8px 12px; 177 | border: none; 178 | color: #334155; 179 | } 180 | 181 | QTableWidget::item:selected { 182 | background: #dbeafe; 183 | color: #1e40af; 184 | } 185 | 186 | /* Table Header - Elegant */ 187 | QHeaderView::section { 188 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 189 | stop: 0 #f8fafc, stop: 1 #f1f5f9); 190 | color: #475569; 191 | border: none; 192 | border-bottom: 2px solid #e2e8f0; 193 | padding: 10px 12px; 194 | font-weight: 600; 195 | font-size: 10pt; 196 | } 197 | 198 | QHeaderView::section:first { 199 | border-top-left-radius: 12px; 200 | } 201 | 202 | QHeaderView::section:last { 203 | border-top-right-radius: 12px; 204 | } 205 | 206 | QTableCornerButton::section { 207 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 208 | stop: 0 #f8fafc, stop: 1 #f1f5f9); 209 | border: none; 210 | border-bottom: 2px solid #e2e8f0; 211 | border-top-left-radius: 12px; 212 | } 213 | 214 | /* Scrollbars - Minimal and Modern */ 215 | QScrollBar:vertical { 216 | background: #f8fafc; 217 | width: 8px; 218 | border-radius: 4px; 219 | margin: 0; 220 | } 221 | 222 | QScrollBar::handle:vertical { 223 | background: #cbd5e1; 224 | border-radius: 4px; 225 | min-height: 20px; 226 | } 227 | 228 | QScrollBar::handle:vertical:hover { 229 | background: #94a3b8; 230 | } 231 | 232 | QScrollBar::add-line:vertical, 233 | QScrollBar::sub-line:vertical { 234 | height: 0px; 235 | } 236 | 237 | QScrollBar:horizontal { 238 | background: #f8fafc; 239 | height: 8px; 240 | border-radius: 4px; 241 | margin: 0; 242 | } 243 | 244 | QScrollBar::handle:horizontal { 245 | background: #cbd5e1; 246 | border-radius: 4px; 247 | min-width: 20px; 248 | } 249 | 250 | QScrollBar::handle:horizontal:hover { 251 | background: #94a3b8; 252 | } 253 | 254 | QScrollBar::add-line:horizontal, 255 | QScrollBar::sub-line:horizontal { 256 | width: 0px; 257 | } 258 | 259 | /* Progress Dialog - Clean */ 260 | QProgressDialog { 261 | background: #ffffff; 262 | border: 1px solid #e2e8f0; 263 | border-radius: 12px; 264 | } 265 | 266 | QProgressBar { 267 | background: #f1f5f9; 268 | border: 1px solid #e2e8f0; 269 | border-radius: 6px; 270 | text-align: center; 271 | color: #475569; 272 | font-weight: 500; 273 | } 274 | 275 | QProgressBar::chunk { 276 | background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, 277 | stop: 0 #3b82f6, stop: 1 #1d4ed8); 278 | border-radius: 5px; 279 | } 280 | 281 | /* Dialog Windows */ 282 | QDialog { 283 | background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, 284 | stop: 0 #ffffff, stop: 1 #f8fafc); 285 | border-radius: 12px; 286 | } 287 | 288 | /* Menu */ 289 | QMenu { 290 | background: #ffffff; 291 | border: 1px solid #e2e8f0; 292 | border-radius: 8px; 293 | padding: 4px; 294 | color: #334155; 295 | } 296 | 297 | QMenu::item { 298 | padding: 8px 16px; 299 | border-radius: 6px; 300 | margin: 2px; 301 | } 302 | 303 | QMenu::item:selected { 304 | background: #f1f5f9; 305 | color: #1e40af; 306 | } 307 | 308 | QMenu::separator { 309 | height: 1px; 310 | background: #e2e8f0; 311 | margin: 4px 8px; 312 | } 313 | 314 | /* Tooltips */ 315 | QToolTip { 316 | background: #1e293b; 317 | color: #f8fafc; 318 | border: none; 319 | border-radius: 6px; 320 | padding: 6px 10px; 321 | font-size: 9pt; 322 | } 323 | -------------------------------------------------------------------------------- /warp_bridge_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | HTTP server for Warp Account Bridge - Chrome extension integration 6 | """ 7 | 8 | import json 9 | import threading 10 | import time 11 | from http.server import HTTPServer, BaseHTTPRequestHandler 12 | try: 13 | # Python 3.7+ 14 | from http.server import ThreadingHTTPServer as _ThreadingHTTPServer 15 | except ImportError: # pragma: no cover 16 | from socketserver import ThreadingMixIn 17 | 18 | class _ThreadingHTTPServer(ThreadingMixIn, HTTPServer): 19 | daemon_threads = True 20 | from urllib.parse import urlparse, parse_qs 21 | import sqlite3 22 | from languages import get_language_manager, _ 23 | 24 | class BridgeRequestHandler(BaseHTTPRequestHandler): 25 | def __init__(self, *args, account_manager=None, on_account_added=None, **kwargs): 26 | self.account_manager = account_manager 27 | self.on_account_added = on_account_added 28 | super().__init__(*args, **kwargs) 29 | 30 | def _set_cors_headers(self): 31 | """CORS headers for browser requests""" 32 | self.send_header('Access-Control-Allow-Origin', '*') 33 | self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') 34 | self.send_header('Access-Control-Allow-Headers', 'Content-Type, X-Extension-ID') 35 | 36 | def _send_json_response(self, status_code, data): 37 | """Send JSON response""" 38 | self.send_response(status_code) 39 | self.send_header('Content-Type', 'application/json') 40 | self._set_cors_headers() 41 | self.end_headers() 42 | response_data = json.dumps(data, ensure_ascii=False) 43 | self.wfile.write(response_data.encode('utf-8')) 44 | 45 | def _verify_extension(self): 46 | """Verify request comes from our extension""" 47 | extension_id = self.headers.get('X-Extension-ID') 48 | return extension_id == 'warp-account-bridge-v1' 49 | 50 | def do_OPTIONS(self): 51 | """Handle CORS preflight requests""" 52 | self.send_response(200) 53 | self._set_cors_headers() 54 | self.end_headers() 55 | 56 | def do_GET(self): 57 | """Handle GET requests""" 58 | parsed_url = urlparse(self.path) 59 | 60 | if parsed_url.path == '/health': 61 | # Health check endpoint 62 | self._send_json_response(200, { 63 | 'status': 'ok', 64 | 'service': 'warp-bridge', 65 | 'timestamp': int(time.time()) 66 | }) 67 | else: 68 | self._send_json_response(404, {'error': 'Endpoint not found'}) 69 | 70 | def do_POST(self): 71 | """Handle POST requests""" 72 | parsed_url = urlparse(self.path) 73 | 74 | if not self._verify_extension(): 75 | self._send_json_response(403, {'error': 'Unauthorized - Invalid extension ID'}) 76 | return 77 | 78 | if parsed_url.path == '/add-account': 79 | self._handle_add_account() 80 | elif parsed_url.path == '/setup-bridge': 81 | self._handle_setup_bridge() 82 | else: 83 | self._send_json_response(404, {'error': 'Endpoint not found'}) 84 | 85 | def _handle_add_account(self): 86 | """Handle account addition from extension""" 87 | try: 88 | # Read request body 89 | content_length = int(self.headers.get('Content-Length', 0)) 90 | if content_length == 0: 91 | self._send_json_response(400, {'error': 'Empty request body'}) 92 | return 93 | 94 | body = self.rfile.read(content_length) 95 | account_data = json.loads(body.decode('utf-8')) 96 | 97 | # Validate account data structure 98 | if not self._validate_account_data(account_data): 99 | self._send_json_response(400, {'error': 'Invalid account data structure'}) 100 | return 101 | 102 | # Convert to JSON string for AccountManager 103 | account_json = json.dumps(account_data, ensure_ascii=False) 104 | 105 | # Add account using AccountManager 106 | if self.account_manager: 107 | success, message = self.account_manager.add_account(account_json) 108 | 109 | if success: 110 | print(f"✅ Bridge: Hesap eklendi - {account_data.get('email', 'Unknown')}") 111 | 112 | # Yanıtı hemen döndür, UI yenilemeyi arka planda tetikle 113 | self._send_json_response(200, { 114 | 'success': True, 115 | 'message': message, 116 | 'email': account_data.get('email') 117 | }) 118 | 119 | if self.on_account_added: 120 | try: 121 | threading.Thread( 122 | target=self.on_account_added, 123 | args=(account_data.get('email'),), 124 | daemon=True 125 | ).start() 126 | except Exception as e: 127 | print(f"⚠️ Tablo güncelleme hatası: {e}") 128 | return 129 | else: 130 | print(f"❌ Bridge: Hesap ekleme hatası - {message}") 131 | self._send_json_response(400, { 132 | 'success': False, 133 | 'error': message 134 | }) 135 | else: 136 | self._send_json_response(500, {'error': 'Account manager not available'}) 137 | 138 | except json.JSONDecodeError: 139 | self._send_json_response(400, {'error': 'Invalid JSON data'}) 140 | except Exception as e: 141 | print(f"❌ Bridge: Add account error - {str(e)}") 142 | self._send_json_response(500, {'error': f'Server error: {str(e)}'}) 143 | 144 | def _handle_setup_bridge(self): 145 | """Handle bridge setup from extension""" 146 | try: 147 | content_length = int(self.headers.get('Content-Length', 0)) 148 | if content_length > 0: 149 | body = self.rfile.read(content_length) 150 | setup_data = json.loads(body.decode('utf-8')) 151 | print(f"🔗 Bridge: Extension connected - ID: {setup_data.get('extensionId', 'Unknown')}") 152 | 153 | self._send_json_response(200, { 154 | 'success': True, 155 | 'message': 'Bridge setup successful', 156 | 'server_version': '1.0' 157 | }) 158 | 159 | except Exception as e: 160 | print(f"❌ Bridge: Setup error - {str(e)}") 161 | self._send_json_response(500, {'error': f'Setup error: {str(e)}'}) 162 | 163 | def _validate_account_data(self, data): 164 | """Validate account data structure""" 165 | try: 166 | # Check required fields 167 | required_fields = ['email', 'stsTokenManager'] 168 | for field in required_fields: 169 | if field not in data: 170 | return False 171 | 172 | # Check stsTokenManager structure 173 | sts_manager = data['stsTokenManager'] 174 | required_sts_fields = ['accessToken', 'refreshToken'] 175 | for field in required_sts_fields: 176 | if field not in sts_manager: 177 | return False 178 | 179 | return True 180 | 181 | except Exception: 182 | return False 183 | 184 | def log_message(self, format, *args): 185 | """Override to reduce log noise""" 186 | pass # Silent logging 187 | 188 | 189 | class WarpBridgeServer: 190 | def __init__(self, account_manager, port=8765, on_account_added=None): 191 | self.account_manager = account_manager 192 | self.port = port 193 | self.server = None 194 | self.server_thread = None 195 | self.running = False 196 | self.on_account_added = on_account_added 197 | 198 | def start(self): 199 | """Start the bridge server""" 200 | try: 201 | # Create request handler with account_manager 202 | def handler(*args, **kwargs): 203 | return BridgeRequestHandler(*args, account_manager=self.account_manager, 204 | on_account_added=self.on_account_added, **kwargs) 205 | 206 | self.server = _ThreadingHTTPServer(('localhost', self.port), handler) 207 | try: 208 | self.server.daemon_threads = True 209 | self.server.allow_reuse_address = True 210 | except Exception: 211 | pass 212 | self.running = True 213 | 214 | # Start server in separate thread 215 | self.server_thread = threading.Thread(target=self._run_server, daemon=True) 216 | self.server_thread.start() 217 | 218 | print(f"🌉 Bridge Server başlatıldı: http://localhost:{self.port}") 219 | return True 220 | 221 | except Exception as e: 222 | print(f"❌ Bridge Server başlatma hatası: {e}") 223 | return False 224 | 225 | def _run_server(self): 226 | """Run the server in thread""" 227 | try: 228 | self.server.serve_forever() 229 | except Exception as e: 230 | if self.running: # Only show error if we're supposed to be running 231 | print(f"❌ Bridge Server çalışma hatası: {e}") 232 | 233 | def stop(self): 234 | """Stop the bridge server""" 235 | if self.server and self.running: 236 | self.running = False 237 | self.server.shutdown() 238 | if self.server_thread: 239 | self.server_thread.join(timeout=2) 240 | print("🛑 Bridge Server durduruldu") 241 | 242 | def is_running(self): 243 | """Check if server is running""" 244 | return self.running and self.server_thread and self.server_thread.is_alive() 245 | 246 | 247 | # Test function 248 | if __name__ == "__main__": 249 | from warp_account_manager import AccountManager 250 | 251 | print("Testing Warp Bridge Server...") 252 | 253 | # Create account manager 254 | account_manager = AccountManager() 255 | 256 | # Start bridge server 257 | bridge = WarpBridgeServer(account_manager) 258 | 259 | if bridge.start(): 260 | print("Bridge server is running. Press Ctrl+C to stop.") 261 | try: 262 | while True: 263 | time.sleep(1) 264 | except KeyboardInterrupt: 265 | bridge.stop() 266 | print("Bridge server stopped.") 267 | else: 268 | print("Failed to start bridge server.") 269 | -------------------------------------------------------------------------------- /chrome-extension/content.js: -------------------------------------------------------------------------------- 1 | // Content script for Warp Account Bridge 2 | // Runs on https://app.warp.dev/logged_in/remote pages 3 | 4 | const BRIDGE_CONFIG = { 5 | pythonAppPort: 8765, 6 | extensionId: "warp-account-bridge-v1", 7 | }; 8 | 9 | let bridgeButton = null; 10 | let isDataExtracted = false; 11 | let isProcessing = false; 12 | 13 | // Check if we can extract data 14 | async function checkDataAvailability() { 15 | try { 16 | const request = indexedDB.open("firebaseLocalStorageDb"); 17 | 18 | return new Promise((resolve) => { 19 | request.onsuccess = function (event) { 20 | const db = event.target.result; 21 | const transaction = db.transaction(["firebaseLocalStorage"], "readonly"); 22 | const objectStore = transaction.objectStore("firebaseLocalStorage"); 23 | 24 | objectStore.getAll().onsuccess = function (event) { 25 | const results = event.target.result; 26 | const hasValidData = results.some((item) => item.value && typeof item.value === "object" && item.value.email && item.value.stsTokenManager); 27 | resolve(hasValidData); 28 | }; 29 | 30 | objectStore.getAll().onerror = () => resolve(false); 31 | }; 32 | 33 | request.onerror = () => resolve(false); 34 | }); 35 | } catch (error) { 36 | console.log("Data check error:", error); 37 | return false; 38 | } 39 | } 40 | 41 | // Extract account data 42 | async function extractAccountData() { 43 | try { 44 | const request = indexedDB.open("firebaseLocalStorageDb"); 45 | 46 | return new Promise((resolve, reject) => { 47 | request.onsuccess = function (event) { 48 | const db = event.target.result; 49 | const transaction = db.transaction(["firebaseLocalStorage"], "readonly"); 50 | const objectStore = transaction.objectStore("firebaseLocalStorage"); 51 | 52 | objectStore.getAll().onsuccess = function (event) { 53 | const results = event.target.result; 54 | 55 | for (let result of results) { 56 | if (result.value && typeof result.value === "object" && result.value.email && result.value.stsTokenManager) { 57 | resolve(result.value); 58 | return; 59 | } 60 | } 61 | reject(new Error("No valid account data found")); 62 | }; 63 | 64 | objectStore.getAll().onerror = () => reject(new Error("Database read error")); 65 | }; 66 | 67 | request.onerror = () => reject(new Error("Database connection error")); 68 | }); 69 | } catch (error) { 70 | throw new Error(`Data extraction failed: ${error.message}`); 71 | } 72 | } 73 | 74 | // Send data to Python app 75 | async function sendDataToPythonApp(accountData) { 76 | try { 77 | console.log("Sending account data to bridge server..."); 78 | const response = await fetch(`http://localhost:${BRIDGE_CONFIG.pythonAppPort}/add-account`, { 79 | method: "POST", 80 | headers: { 81 | "Content-Type": "application/json", 82 | "X-Extension-ID": BRIDGE_CONFIG.extensionId, 83 | }, 84 | body: JSON.stringify(accountData), 85 | }); 86 | 87 | if (response.ok) { 88 | const result = await response.json(); 89 | console.log("Account successfully sent to bridge server"); 90 | return { success: true, message: result.message || "Account added successfully" }; 91 | } else { 92 | const error = await response.text(); 93 | console.error("Bridge server returned error:", error); 94 | return { success: false, message: `Server error: ${error}` }; 95 | } 96 | } catch (error) { 97 | console.error("Bridge connection error:", error); 98 | if (error.message.includes("Failed to fetch") || error.message.includes("ERR_CONNECTION_REFUSED")) { 99 | return { success: false, message: "Bridge server not running. Please start Warp Account Manager first!" }; 100 | } 101 | return { success: false, message: `Connection failed: ${error.message}` }; 102 | } 103 | } 104 | 105 | // Create and show bridge button 106 | function createBridgeButton() { 107 | if (bridgeButton) return; 108 | 109 | bridgeButton = document.createElement("div"); 110 | bridgeButton.id = "warp-bridge-button"; 111 | bridgeButton.innerHTML = ` 112 |
138 |
139 |
Add to Warp Manager
140 |
141 | `; 142 | 143 | bridgeButton.addEventListener("click", handleButtonClick); 144 | document.body.appendChild(bridgeButton); 145 | } 146 | 147 | // Handle button click 148 | async function handleButtonClick() { 149 | // Prevent multiple simultaneous clicks 150 | if (isProcessing || bridgeButton.style.pointerEvents === "none") { 151 | console.log("Already processing or button disabled"); 152 | return; 153 | } 154 | 155 | // Set processing flag immediately 156 | isProcessing = true; 157 | 158 | const buttonText = bridgeButton.querySelector(".text"); 159 | const originalText = buttonText.textContent; 160 | 161 | // Disable button immediately 162 | bridgeButton.style.pointerEvents = "none"; 163 | 164 | try { 165 | console.log("Starting account extraction process..."); 166 | buttonText.textContent = "Extracting data..."; 167 | 168 | // Check data availability first 169 | const hasData = await checkDataAvailability(); 170 | if (!hasData) { 171 | throw new Error("No account data available"); 172 | } 173 | 174 | const accountData = await extractAccountData(); 175 | console.log("Account data extracted successfully"); 176 | 177 | buttonText.textContent = "Sending to app..."; 178 | const result = await sendDataToPythonApp(accountData); 179 | 180 | if (result.success) { 181 | console.log("Account added successfully via bridge"); 182 | buttonText.textContent = "✅ Added successfully!"; 183 | bridgeButton.style.background = "linear-gradient(135deg, #4CAF50 0%, #45a049 100%)"; 184 | isDataExtracted = true; 185 | 186 | // Save extraction state to localStorage with session-specific keys 187 | const urlParams = new URLSearchParams(window.location.search); 188 | const apiKey = urlParams.get("apiKey") || "default"; 189 | const oobCode = urlParams.get("oobCode") || "default"; 190 | const sessionKey = `warp_session_${apiKey}_${oobCode}`; 191 | 192 | localStorage.setItem(`${sessionKey}_time`, Date.now().toString()); 193 | localStorage.setItem(`${sessionKey}_email`, accountData.email || "unknown"); 194 | console.log(`Session cache set: ${sessionKey} -> ${accountData.email || "unknown"}`); 195 | 196 | // Hide button after 3 seconds 197 | setTimeout(() => { 198 | if (bridgeButton) { 199 | bridgeButton.style.opacity = "0"; 200 | setTimeout(() => { 201 | if (bridgeButton && bridgeButton.parentNode) { 202 | bridgeButton.parentNode.removeChild(bridgeButton); 203 | bridgeButton = null; 204 | } 205 | // Reset processing flag after cleanup 206 | isProcessing = false; 207 | }, 300); 208 | } 209 | }, 3000); 210 | } else { 211 | throw new Error(result.message); 212 | } 213 | } catch (error) { 214 | console.error("Bridge error:", error); 215 | buttonText.textContent = "❌ Error: " + error.message; 216 | bridgeButton.style.background = "linear-gradient(135deg, #f44336 0%, #d32f2f 100%)"; 217 | 218 | // Reset button after 5 seconds 219 | setTimeout(() => { 220 | if (bridgeButton) { 221 | buttonText.textContent = originalText; 222 | bridgeButton.style.background = "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"; 223 | bridgeButton.style.pointerEvents = "auto"; 224 | } 225 | // Reset processing flag 226 | isProcessing = false; 227 | }, 5000); 228 | } 229 | } 230 | 231 | // Initialize bridge 232 | async function initBridge() { 233 | // Wait for page to be fully loaded 234 | if (document.readyState === "loading") { 235 | document.addEventListener("DOMContentLoaded", initBridge); 236 | return; 237 | } 238 | 239 | // Remove existing button if any 240 | if (bridgeButton && bridgeButton.parentNode) { 241 | bridgeButton.parentNode.removeChild(bridgeButton); 242 | bridgeButton = null; 243 | } 244 | 245 | // Check which page we're on and show appropriate button 246 | const currentUrl = window.location.href; 247 | 248 | console.log(`Current URL: ${currentUrl}`); 249 | 250 | // Clear old global cache entries (migration) 251 | const oldKeys = ["warp_account_extracted", "warp_account_extracted_time", "warp_account_extracted_email"]; 252 | oldKeys.forEach((key) => { 253 | if (localStorage.getItem(key)) { 254 | localStorage.removeItem(key); 255 | console.log(`Cleared old global cache: ${key}`); 256 | } 257 | }); 258 | 259 | if (currentUrl.includes("app.warp.dev/logged_in") || currentUrl.includes("app.warp.dev/login")) { 260 | // All logged in pages - show bridge button 261 | console.log("Logged in page detected - checking for account data..."); 262 | 263 | // Wait a bit for the page to settle and check multiple times 264 | let attempts = 0; 265 | const maxAttempts = 5; 266 | 267 | const checkAndShow = async () => { 268 | try { 269 | // Generate unique key for this session/URL 270 | const urlParams = new URLSearchParams(window.location.search); 271 | const apiKey = urlParams.get("apiKey") || "default"; 272 | const oobCode = urlParams.get("oobCode") || "default"; 273 | const sessionKey = `warp_session_${apiKey}_${oobCode}`; 274 | 275 | // Check if this specific session was already processed 276 | const lastExtracted = localStorage.getItem(`${sessionKey}_time`); 277 | const lastEmail = localStorage.getItem(`${sessionKey}_email`); 278 | 279 | // Clear old cache entries (older than 1 hour) 280 | const cutoffTime = Date.now() - 60 * 60 * 1000; 281 | for (let i = localStorage.length - 1; i >= 0; i--) { 282 | const key = localStorage.key(i); 283 | if (key && key.startsWith("warp_session_") && key.endsWith("_time")) { 284 | const time = localStorage.getItem(key); 285 | if (time && parseInt(time) < cutoffTime) { 286 | localStorage.removeItem(key); 287 | localStorage.removeItem(key.replace("_time", "_email")); 288 | console.log(`Cleared old cache for ${key}`); 289 | } 290 | } 291 | } 292 | 293 | if (lastExtracted && Date.now() - parseInt(lastExtracted) < 5 * 60 * 1000) { 294 | console.log(`This session was recently processed (${lastEmail || "unknown"}), skipping button creation`); 295 | return false; 296 | } 297 | 298 | console.log(`Checking data availability (attempt ${attempts + 1}/${maxAttempts})...`); 299 | const hasData = await checkDataAvailability(); 300 | console.log(`Data available: ${hasData}, isDataExtracted: ${isDataExtracted}, bridgeButton exists: ${!!bridgeButton}`); 301 | 302 | if (hasData && !isDataExtracted && !bridgeButton) { 303 | console.log("Creating bridge button..."); 304 | createBridgeButton(); 305 | return true; 306 | } else if (!hasData && attempts < maxAttempts) { 307 | attempts++; 308 | setTimeout(checkAndShow, 1000); 309 | } else { 310 | console.log("Stopping attempts - either data not available or max attempts reached"); 311 | } 312 | } catch (error) { 313 | console.log("Bridge init error:", error); 314 | if (attempts < maxAttempts) { 315 | attempts++; 316 | setTimeout(checkAndShow, 1000); 317 | } 318 | } 319 | return false; 320 | }; 321 | 322 | setTimeout(checkAndShow, 2000); 323 | } 324 | } 325 | 326 | // Start the bridge 327 | initBridge(); 328 | 329 | // Also listen for navigation changes (SPA) 330 | let lastUrl = location.href; 331 | new MutationObserver(() => { 332 | const url = location.href; 333 | if (url !== lastUrl) { 334 | lastUrl = url; 335 | // Page changed, reinitialize if needed 336 | setTimeout(initBridge, 1000); 337 | } 338 | }).observe(document, { subtree: true, childList: true }); 339 | -------------------------------------------------------------------------------- /warp_proxy_script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Mitmproxy script for intercepting and modifying Warp API requests 6 | """ 7 | 8 | import json 9 | import sqlite3 10 | import time 11 | import urllib3 12 | import re 13 | import random 14 | import string 15 | from mitmproxy import http 16 | from mitmproxy.script import concurrent 17 | from languages import get_language_manager, _ 18 | 19 | # SSL uyarılarını gizle 20 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 21 | 22 | 23 | def randomize_uuid_string(uuid_str): 24 | """ 25 | UUID string'ini rastgele değiştir - harfler hexadecimal harflerle, sayılar rastgele sayılarla değiştirilir 26 | Tire (-) karakterleri korunur, büyük/küçük harf formatı korunur 27 | 28 | Args: 29 | uuid_str (str): UUID formatındaki string (örn: 4d22323e-1ce9-44c1-a922-112a718ea3fc) 30 | 31 | Returns: 32 | str: Rastgele değiştirilmiş UUID string 33 | """ 34 | hex_digits_lower = '0123456789abcdef' 35 | hex_digits_upper = '0123456789ABCDEF' 36 | 37 | result = [] 38 | for char in uuid_str: 39 | if char == '-': 40 | # Tire karakterini koru 41 | result.append(char) 42 | elif char.isdigit(): 43 | # Sayıyı rastgele hexadecimal karakter ile değiştir (sayı veya a-f) 44 | result.append(random.choice(hex_digits_lower)) 45 | elif char in 'abcdef': 46 | # Küçük hexadecimal harfi rastgele küçük hexadecimal harf ile değiştir 47 | result.append(random.choice(hex_digits_lower)) 48 | elif char in 'ABCDEF': 49 | # Büyük hexadecimal harfi rastgele büyük hexadecimal harf ile değiştir 50 | result.append(random.choice(hex_digits_upper)) 51 | else: 52 | # Diğer karakterleri olduğu gibi bırak (güvenlik için) 53 | result.append(char) 54 | 55 | return ''.join(result) 56 | 57 | 58 | def generate_experiment_id(): 59 | """Warp Experiment ID formatında UUID üret""" 60 | # 931df166-756c-4d4c-b486-4231224bc531 formatında 61 | # 8-4-4-4-12 hex karakter yapısı 62 | def hex_chunk(length): 63 | return ''.join(random.choice('0123456789abcdef') for _ in range(length)) 64 | 65 | return f"{hex_chunk(8)}-{hex_chunk(4)}-{hex_chunk(4)}-{hex_chunk(4)}-{hex_chunk(12)}" 66 | 67 | class WarpProxyHandler: 68 | def __init__(self): 69 | self.db_path = "accounts.db" 70 | self.active_token = None 71 | self.active_email = None 72 | self.token_expiry = None 73 | self.last_trigger_check = 0 74 | self.last_token_check = 0 75 | self.user_settings_cache = None 76 | 77 | def get_active_account(self): 78 | """Aktif hesabı veritabanından al""" 79 | try: 80 | conn = sqlite3.connect(self.db_path) 81 | cursor = conn.cursor() 82 | 83 | # Önce aktif hesabı al 84 | cursor.execute('SELECT value FROM proxy_settings WHERE key = ?', ('active_account',)) 85 | active_result = cursor.fetchone() 86 | 87 | if active_result: 88 | active_email = active_result[0] 89 | # Sonra hesap verilerini al 90 | cursor.execute('SELECT account_data FROM accounts WHERE email = ?', (active_email,)) 91 | account_result = cursor.fetchone() 92 | 93 | if account_result: 94 | account_data = json.loads(account_result[0]) 95 | conn.close() 96 | return active_email, account_data 97 | 98 | conn.close() 99 | return None, None 100 | except Exception as e: 101 | print(f"Aktif hesap alma hatası: {e}") 102 | return None, None 103 | 104 | def update_active_token(self): 105 | """Aktif hesabın token bilgilerini güncelle""" 106 | try: 107 | print("🔍 Aktif hesap kontrol ediliyor...") 108 | email, account_data = self.get_active_account() 109 | if not account_data: 110 | print("❌ Aktif hesap bulunamadı") 111 | self.active_token = None 112 | self.active_email = None 113 | return False 114 | 115 | old_email = self.active_email 116 | 117 | current_time = int(time.time() * 1000) 118 | token_expiry = account_data['stsTokenManager']['expirationTime'] 119 | 120 | # Token süresi 1 dakikadan az kaldıysa yenile 121 | if current_time >= (token_expiry - 60000): # 1 dakika = 60000ms 122 | print(f"Token yenileniyor: {email}") 123 | if self.refresh_token(email, account_data): 124 | # Güncellenmiş verileri al 125 | email, account_data = self.get_active_account() 126 | if account_data: 127 | self.active_token = account_data['stsTokenManager']['accessToken'] 128 | self.token_expiry = account_data['stsTokenManager']['expirationTime'] 129 | self.active_email = email 130 | print(f"Token yenilendi: {email}") 131 | return True 132 | return False 133 | else: 134 | self.active_token = account_data['stsTokenManager']['accessToken'] 135 | self.token_expiry = token_expiry 136 | self.active_email = email 137 | 138 | if old_email != email: 139 | print(f"🔄 Aktif hesap değişti: {old_email} → {email}") 140 | else: 141 | print(f"✅ Token aktif: {email}") 142 | return True 143 | except Exception as e: 144 | print(f"Token güncelleme hatası: {e}") 145 | return False 146 | 147 | def check_account_change_trigger(self): 148 | """Hesap değişiklik trigger dosyasını kontrol et""" 149 | try: 150 | trigger_file = "account_change_trigger.tmp" 151 | import os 152 | 153 | if os.path.exists(trigger_file): 154 | # Dosyanın değiştirilme zamanını kontrol et 155 | mtime = os.path.getmtime(trigger_file) 156 | print(f"📁 Trigger dosyası bulundu - mtime: {mtime}, last_check: {self.last_trigger_check}") 157 | if mtime > self.last_trigger_check: 158 | print("🔄 Hesap değişiklik trigger tespit edildi!") 159 | self.last_trigger_check = mtime 160 | 161 | # Trigger dosyasını sil 162 | try: 163 | os.remove(trigger_file) 164 | print("🗑️ Trigger dosyası silindi") 165 | except Exception as e: 166 | print(f"Trigger dosyası silinme hatası: {e}") 167 | 168 | # Token güncelle 169 | print("🔄 Token güncelleniyor...") 170 | self.update_active_token() 171 | return True 172 | else: 173 | print("⏸️ Trigger dosyası zaten işlenmiş, atlanıyor") 174 | return False 175 | except Exception as e: 176 | print(f"Trigger kontrol hatası: {e}") 177 | return False 178 | 179 | def refresh_token(self, email, account_data): 180 | """Firebase token yenileme""" 181 | try: 182 | import requests 183 | 184 | refresh_token = account_data['stsTokenManager']['refreshToken'] 185 | api_key = account_data['apiKey'] 186 | 187 | url = f"https://securetoken.googleapis.com/v1/token?key={api_key}" 188 | data = { 189 | 'grant_type': 'refresh_token', 190 | 'refresh_token': refresh_token 191 | } 192 | 193 | # Proxy kullanmadan direkt bağlan 194 | proxies = {'http': None, 'https': None} 195 | response = requests.post(url, json=data, timeout=30, verify=False, proxies=proxies) 196 | 197 | if response.status_code == 200: 198 | token_data = response.json() 199 | new_token_data = { 200 | 'accessToken': token_data['access_token'], 201 | 'refreshToken': token_data['refresh_token'], 202 | 'expirationTime': int(time.time() * 1000) + (int(token_data['expires_in']) * 1000) 203 | } 204 | 205 | # Veritabanını güncelle 206 | conn = sqlite3.connect(self.db_path) 207 | cursor = conn.cursor() 208 | cursor.execute('SELECT account_data FROM accounts WHERE email = ?', (email,)) 209 | result = cursor.fetchone() 210 | 211 | if result: 212 | account_data = json.loads(result[0]) 213 | account_data['stsTokenManager'].update(new_token_data) 214 | 215 | cursor.execute(''' 216 | UPDATE accounts SET account_data = ?, last_updated = CURRENT_TIMESTAMP 217 | WHERE email = ? 218 | ''', (json.dumps(account_data), email)) 219 | conn.commit() 220 | 221 | conn.close() 222 | return True 223 | return False 224 | except Exception as e: 225 | print(f"Token yenileme hatası: {e}") 226 | return False 227 | 228 | def mark_account_as_banned(self, email): 229 | """Hesabı banlanmış olarak işaretle""" 230 | try: 231 | conn = sqlite3.connect(self.db_path) 232 | cursor = conn.cursor() 233 | 234 | # Hesabın health_status'unu 'banned' olarak güncelle 235 | cursor.execute(''' 236 | UPDATE accounts SET health_status = 'banned', last_updated = CURRENT_TIMESTAMP 237 | WHERE email = ? 238 | ''', (email,)) 239 | conn.commit() 240 | conn.close() 241 | 242 | print(f"Hesap banlanmış olarak işaretlendi: {email}") 243 | 244 | # Aktif hesabı temizle (banlanmış hesap aktif kalamaz) 245 | conn = sqlite3.connect(self.db_path) 246 | cursor = conn.cursor() 247 | cursor.execute('DELETE FROM proxy_settings WHERE key = ?', ('active_account',)) 248 | conn.commit() 249 | conn.close() 250 | 251 | # Handler'daki aktif hesap bilgilerini temizle 252 | self.active_token = None 253 | self.active_email = None 254 | self.token_expiry = None 255 | 256 | print("Banlanmış hesap aktif hesap listesinden çıkarıldı") 257 | 258 | # GUI'ye ban bildirimini gönder 259 | self.notify_gui_about_ban(email) 260 | return True 261 | 262 | except Exception as e: 263 | print(f"Hesap ban işaretleme hatası: {e}") 264 | return False 265 | 266 | def notify_gui_about_ban(self, email): 267 | """GUI'ye ban bildirimini dosya üzerinden gönder""" 268 | try: 269 | import os 270 | import time 271 | 272 | # Ban bildirim dosyası oluştur 273 | ban_notification_file = "ban_notification.tmp" 274 | with open(ban_notification_file, 'w', encoding='utf-8') as f: 275 | f.write(f"{email}|{int(time.time())}") 276 | 277 | print(f"Ban bildirimi dosyası oluşturuldu: {ban_notification_file}") 278 | except Exception as e: 279 | print(f"Ban bildirimi gönderme hatası: {e}") 280 | 281 | def load_user_settings(self): 282 | """user_settings.json dosyasını yükle""" 283 | try: 284 | import os 285 | if os.path.exists("user_settings.json"): 286 | with open("user_settings.json", 'r', encoding='utf-8') as f: 287 | self.user_settings_cache = json.load(f) 288 | print("✅ user_settings.json dosyası başarıyla yüklendi") 289 | return True 290 | else: 291 | print("⚠️ user_settings.json dosyası bulunamadı") 292 | self.user_settings_cache = None 293 | return False 294 | except Exception as e: 295 | print(f"user_settings.json yükleme hatası: {e}") 296 | self.user_settings_cache = None 297 | return False 298 | 299 | def refresh_user_settings(self): 300 | """user_settings.json dosyasını yeniden yükle""" 301 | print("🔄 user_settings.json yeniden yükleniyor...") 302 | return self.load_user_settings() 303 | 304 | # Global handler instance 305 | handler = WarpProxyHandler() 306 | 307 | def is_relevant_request(flow: http.HTTPFlow) -> bool: 308 | """İsteğin bizi ilgilendirip ilgilendirmediğini kontrol et""" 309 | 310 | # Firebase token yenileme isteklerini User-Agent ile kontrol et ve hariç tut 311 | if ("securetoken.googleapis.com" in flow.request.pretty_host and 312 | flow.request.headers.get("User-Agent") == "WarpAccountManager/1.0"): 313 | return False 314 | 315 | # WarpAccountManager'dan gelen istekleri kontrol et ve hariç tut 316 | if flow.request.headers.get("X-Warp-Manager-Request") == "true": 317 | return False 318 | 319 | # Sadece belirli domainleri işle 320 | relevant_domains = [ 321 | "app.warp.dev", 322 | "dataplane.rudderstack.com" # Bloklamak için 323 | ] 324 | 325 | # Warp ile ilgili olmayan istekleri sessizce geçir (internet erişimini engelleme) 326 | if not any(domain in flow.request.pretty_host for domain in relevant_domains): 327 | return False 328 | 329 | return True 330 | 331 | @concurrent 332 | def request(flow: http.HTTPFlow) -> None: 333 | """İstek yakalandığında çalışır""" 334 | 335 | # İlgisiz istekleri hemen filtrele - sessizce geç (internet erişimine müdahale etme) 336 | if not is_relevant_request(flow): 337 | # Warp ile ilgili olmayan tüm trafiği direkt geçir 338 | return 339 | 340 | request_url = flow.request.pretty_url 341 | 342 | # *.dataplane.rudderstack.com isteklerini blokla 343 | if "dataplane.rudderstack.com" in flow.request.pretty_host: 344 | print(f"🚫 Rudderstack isteği bloklandı: {request_url}") 345 | flow.response = http.Response.make( 346 | 204, # No Content 347 | b"", 348 | {"Content-Type": "text/plain"} 349 | ) 350 | return 351 | 352 | print(f"🌐 Warp isteği: {flow.request.method} {flow.request.pretty_url}") 353 | 354 | # CreateGenericStringObject isteği tespiti - user_settings.json güncelleme trigger'ı 355 | if ("/graphql/v2?op=CreateGenericStringObject" in request_url and 356 | flow.request.method == "POST"): 357 | print("🔄 CreateGenericStringObject isteği tespit edildi - user_settings.json güncelleniyor...") 358 | handler.refresh_user_settings() 359 | 360 | # Hesap değişiklik trigger kontrolü (her request'te) 361 | if handler.check_account_change_trigger(): 362 | print("🔄 Trigger tespit edildi ve token güncellendi!") 363 | 364 | # Aktif hesap bilgisini göster 365 | print(f"📧 Şu anki aktif hesap: {handler.active_email}") 366 | 367 | # Her dakika token kontrolü yap 368 | current_time = time.time() 369 | if current_time - handler.last_token_check > 60: # 60 saniye 370 | print("⏰ Token kontrol zamanı geldi, güncelleniyor...") 371 | handler.update_active_token() 372 | handler.last_token_check = current_time 373 | 374 | # Aktif hesap kontrolü 375 | if not handler.active_email: 376 | print("❓ Aktif hesap bulunamadı, token kontrol ediliyor...") 377 | handler.update_active_token() 378 | 379 | # Authorization header'ını değiştir 380 | if handler.active_token: 381 | old_auth = flow.request.headers.get("Authorization", "Yok") 382 | new_auth = f"Bearer {handler.active_token}" 383 | flow.request.headers["Authorization"] = new_auth 384 | 385 | print(f"🔑 Authorization header güncellendi: {handler.active_email}") 386 | 387 | # Token'ların gerçekten farklı olup olmadığını kontrol et 388 | if old_auth == new_auth: 389 | print(" ⚠️ UYARI: Eski ve yeni token AYNI!") 390 | else: 391 | print(" ✅ Token başarıyla değiştirildi") 392 | 393 | # Token'ın son kısmını da göster 394 | if len(handler.active_token) > 100: 395 | print(f" Token sonu: ...{handler.active_token[-20:]}") 396 | 397 | else: 398 | print("❌ AKTİF TOKEN BULUNAMADI - HEADER DEĞİŞTİRİLMEDİ!") 399 | print(f" Aktif email: {handler.active_email}") 400 | print(f" Token durumu: {handler.active_token is not None}") 401 | 402 | # Tüm app.warp.dev istekleri için X-Warp-Experiment-Id header'ını kontrol et ve randomize et 403 | existing_experiment_id = flow.request.headers.get("X-Warp-Experiment-Id") 404 | if existing_experiment_id: 405 | new_experiment_id = generate_experiment_id() 406 | flow.request.headers["X-Warp-Experiment-Id"] = new_experiment_id 407 | 408 | print(f"🧪 Experiment ID değiştirildi ({flow.request.path}):") 409 | 410 | def responseheaders(flow: http.HTTPFlow) -> None: 411 | """Response headers alındığında çalışır - streaming'i kontrol eder""" 412 | # İlgisiz istekleri hemen filtrele - sessizce geç 413 | if not is_relevant_request(flow): 414 | return 415 | 416 | # /ai/multi-agent endpoint'i için streaming'i etkinleştir 417 | if "/ai/multi-agent" in flow.request.path: 418 | flow.response.stream = True 419 | print(f"[{time.strftime('%H:%M:%S')}] Streaming etkinleştirildi: {flow.request.pretty_url}") 420 | else: 421 | flow.response.stream = False 422 | 423 | @concurrent 424 | def response(flow: http.HTTPFlow) -> None: 425 | """Yanıt alındığında çalışır""" 426 | 427 | # Firebase token yenileme isteklerini User-Agent ile kontrol et 428 | if ("securetoken.googleapis.com" in flow.request.pretty_host and 429 | flow.request.headers.get("User-Agent") == "WarpAccountManager/1.0"): 430 | return 431 | 432 | # Sadece app.warp.dev domainini işle 433 | if "app.warp.dev" not in flow.request.pretty_host: 434 | return 435 | 436 | # İlgisiz istekleri hemen filtrele - sessizce geç (internet erişimine müdahale etme) 437 | if not is_relevant_request(flow): 438 | return 439 | 440 | # WarpAccountManager'dan gelen istekleri hariç tut 441 | if flow.request.headers.get("X-Warp-Manager-Request") == "true": 442 | return 443 | 444 | print(f"📡 Warp yanıtı: {flow.response.status_code} - {flow.request.pretty_url}") 445 | 446 | # GetUpdatedCloudObjects isteği için cached response kullan 447 | if ("/graphql/v2?op=GetUpdatedCloudObjects" in flow.request.pretty_url and 448 | flow.request.method == "POST" and 449 | flow.response.status_code == 200 and 450 | handler.user_settings_cache is not None): 451 | print("🔄 GetUpdatedCloudObjects response'u cached veriler ile değiştiriliyor...") 452 | try: 453 | # Cached veriyi JSON string'e çevir 454 | cached_response = json.dumps(handler.user_settings_cache, ensure_ascii=False) 455 | 456 | # Response'u değiştir 457 | flow.response.content = cached_response.encode('utf-8') 458 | flow.response.headers["Content-Length"] = str(len(flow.response.content)) 459 | flow.response.headers["Content-Type"] = "application/json" 460 | 461 | print("✅ GetUpdatedCloudObjects response'u başarıyla değiştirildi") 462 | except Exception as e: 463 | print(f"❌ Response değiştirme hatası: {e}") 464 | 465 | # /ai/multi-agent endpoint'inde 403 hatası - hesap banlanmış 466 | if "/ai/multi-agent" in flow.request.path and flow.response.status_code == 403: 467 | print("⛔ 403 FORBIDDEN - Hesap banlanmış tespit edildi!") 468 | if handler.active_email: 469 | print(f"Banlanmış hesap: {handler.active_email}") 470 | handler.mark_account_as_banned(handler.active_email) 471 | else: 472 | print("Aktif hesap bulunamadı, ban işareti konulamadı") 473 | 474 | # Eğer 401 hatası alındıysa token yenilemeyi dene 475 | if flow.response.status_code == 401: 476 | print("401 hatası alındı, token yenileniyor...") 477 | if handler.update_active_token(): 478 | print("Token yenilendi, isteği tekrar dene") 479 | 480 | # Başlangıçta aktif hesabı yükle 481 | def load(loader): 482 | """Script başladığında çalışır""" 483 | print("Warp Proxy Script başlatıldı") 484 | print("Veritabanı bağlantısı kontrol ediliyor...") 485 | handler.update_active_token() 486 | if handler.active_email: 487 | print(f"Aktif hesap yüklendi: {handler.active_email}") 488 | print(f"Token var: {handler.active_token is not None}") 489 | else: 490 | print("Aktif hesap bulunamadı - Bir hesabı aktif etmeyi unutmayın!") 491 | 492 | # user_settings.json dosyasını yükle 493 | print("user_settings.json dosyası yükleniyor...") 494 | handler.load_user_settings() 495 | 496 | def done(): 497 | """Script durdurulduğunda çalışır""" 498 | print("Warp Proxy Script durduruldu") 499 | -------------------------------------------------------------------------------- /languages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import json 5 | import locale 6 | import os 7 | 8 | class LanguageManager: 9 | """Çok dilli destek yöneticisi""" 10 | 11 | def __init__(self): 12 | self.current_language = self.detect_system_language() 13 | self.translations = self.load_translations() 14 | 15 | def detect_system_language(self): 16 | """Sistem dilini otomatik tespit et""" 17 | try: 18 | # Sistem dilini al (Python 3.15 uyumlu) 19 | try: 20 | system_locale = locale.getlocale()[0] 21 | except: 22 | # Fallback için eski metod 23 | import warnings 24 | with warnings.catch_warnings(): 25 | warnings.simplefilter("ignore") 26 | system_locale = locale.getdefaultlocale()[0] 27 | 28 | if system_locale: 29 | # Türkçe veya Azerice ise Türkçe (büyük/küçük harf duyarsız) 30 | locale_lower = system_locale.lower() 31 | if (locale_lower.startswith(('tr', 'az')) or 32 | 'turkish' in locale_lower or 33 | 'türk' in locale_lower): 34 | return 'tr' 35 | 36 | # Varsayılan olarak İngilizce 37 | return 'en' 38 | except: 39 | return 'en' 40 | 41 | def load_translations(self): 42 | """Çeviri dosyalarını yükle""" 43 | translations = { 44 | 'tr': { 45 | # Genel 46 | 'app_title': 'Warp Hesap Yöneticisi', 47 | 'yes': 'Evet', 48 | 'no': 'Hayır', 49 | 'ok': 'Tamam', 50 | 'cancel': 'İptal', 51 | 'close': 'Kapat', 52 | 'error': 'Hata', 53 | 'success': 'Başarılı', 54 | 'warning': 'Uyarı', 55 | 'info': 'Bilgi', 56 | 57 | # Butonlar 58 | 'proxy_start': 'Proxy Başlat', 59 | 'proxy_stop': 'Proxy Durdur', 60 | 'proxy_active': 'Proxy Aktif', 61 | 'add_account': 'Hesap Ekle', 62 | 'refresh_limits': 'Limitleri Yenile', 63 | 'help': 'Yardım', 64 | 'activate': '🟢 Aktif Et', 65 | 'deactivate': '🔴 Deaktif Et', 66 | 'delete_account': '🗑️ Hesabı Sil', 67 | 'create_account': '🌐 Hesap Oluştur', 68 | 'add': 'Ekle', 69 | 'copy_javascript': '📋 JavaScript Kodunu Kopyala', 70 | 'copied': '✅ Kopyalandı!', 71 | 'copy_error': '❌ Hata!', 72 | 'open_certificate': '📁 Sertifika Dosyasını Aç', 73 | 'installation_complete': '✅ Kurulumu Tamamladım', 74 | 75 | # Tablo başlıkları 76 | 'current': 'Güncel', 77 | 'email': 'Email', 78 | 'status': 'Durum', 79 | 'limit': 'Limit', 80 | 81 | # Aktivasyon buton metinleri 82 | 'button_active': 'AKTİF', 83 | 'button_inactive': 'PASİF', 84 | 'button_banned': 'BAN', 85 | 'button_start': 'Başlat', 86 | 'button_stop': 'Durdur', 87 | 88 | # Durum mesajları 89 | 'status_active': 'Aktif', 90 | 'status_banned': 'BAN', 91 | 'status_token_expired': 'Token Süresi Dolmuş', 92 | 'status_proxy_active': ' (Proxy Aktif)', 93 | 'status_error': 'Hata', 94 | 'status_na': 'N/A', 95 | 'status_not_updated': 'Güncellenmedi', 96 | 'status_healthy': 'healthy', 97 | 'status_unhealthy': 'unhealthy', 98 | 'status_banned_key': 'banned', 99 | 100 | # Hesap ekleme 101 | 'add_account_title': 'Hesap Ekle', 102 | 'add_account_instruction': 'Hesap JSON verilerini aşağıya yapıştırın:', 103 | 'add_account_placeholder': 'JSON verilerini buraya yapıştırın...', 104 | 'how_to_get_json': '❓ JSON bilgilerini nasıl alırım?', 105 | 'how_to_get_json_close': '❌ Kapat', 106 | 'json_info_title': 'JSON Bilgilerini Nasıl Alırım?', 107 | 108 | # Hesap ekleme diyalogu tabları 109 | 'tab_manual': 'Manuel', 110 | 'tab_auto': 'Otomatik', 111 | 'manual_method_title': 'Manuel JSON Ekleme', 112 | 'auto_method_title': 'Chrome Eklentisi ile Otomatik Ekleme', 113 | 114 | # Chrome eklentisi açıklaması 115 | 'chrome_extension_title': '🌐 Chrome Eklentisi', 116 | 'chrome_extension_description': 'Chrome eklentimizi kullanarak hesaplarınızı otomatik olarak ekleyebilirsiniz. Bu yöntem daha hızlı ve kolaydır.', 117 | 'chrome_extension_step_1': 'Adım 1: Chrome eklentisini manuel olarak yükleyin', 118 | 'chrome_extension_step_2': 'Adım 2: Warp.dev sitesine gidin ve yeni hesap oluşturun', 119 | 'chrome_extension_step_3': 'Adım 3: Hesap oluşturduktan sonra yönlendirilen sayfada eklenti butonuna tıklayın', 120 | 'chrome_extension_step_4': 'Adım 4: Eklenti hesabı otomatik olarak bu programa ekleyecektir', 121 | 122 | # JSON alma adımları 123 | 'step_1': 'Adım 1: Warp web sitesine gidin ve giriş yapın', 124 | 'step_2': 'Adım 2: Tarayıcı geliştirici konsolunu açın (F12)', 125 | 'step_3': 'Adım 3: Console sekmesine gidin', 126 | 'step_4': 'Adım 4: Aşağıdaki JavaScript kodunu konsola yapıştırın', 127 | 'step_5': 'Adım 5: Enter tuşuna basın', 128 | 'step_6': 'Adım 6: Sayfada çıkan butona tıklayın', 129 | 'step_7': 'Adım 7: Kopyalanan JSON\'u buraya yapıştırın', 130 | 131 | # Yardım 132 | 'help_title': '📖 Warp Hesap Yöneticisi - Kullanım Kılavuzu', 133 | 'help_what_is': '🎯 Bu Yazılım Ne İşe Yarar?', 134 | 'help_what_is_content': 'Warp.dev kod editörünü ücretsiz şekilde kullanabilmek için oluşturacağınız hesaplar arasında kalan limitlerinizi görebilir ve kolayca başlat butonuyla geçiş yapabilirsiniz. Her işleminizde farklı ID kullanarak banlanmanızı engeller.', 135 | 'help_how_works': '⚙️ Nasıl Çalışır?', 136 | 'help_how_works_content': 'Proxy kullanarak Warp editörünün yaptığı istekleri değiştirir. Seçtiğiniz hesabın bilgilerini ve farklı kullanıcı ID\'lerini kullanarak işlemleri gerçekleştirir.', 137 | 'help_how_to_use': '📝 Nasıl Kullanılır?', 138 | 'help_how_to_use_content': '''İlk Kurulum:
139 | Proxy ile çalıştığı için ilk açılışta size belirtilen sertifikayı bilgisayarınızda güvenilen kök sertifikası alanında kurmanız beklenir. Talimatları tamamladıktan sonra Warp editörünü açarak herhangi bir hesaba giriş yaparsınız. İlk başta editör üzerinden bir hesaba giriş yapmanız zorunludur.

140 | 141 | Hesap Ekleme (2 Yöntem):
142 | 1. Chrome Eklentisi: Eklentimizi Chrome'a kurun. Warp.dev'de hesap oluşturduktan sonra yönlendirilen sayfada eklenti butonu belirir, tek tıkla hesap otomatik eklenir.
143 | 2. Manuel Yöntem: Hesap oluşturma sayfasında F12 ile konsolu açın, JavaScript kodunu yapıştırın ve JSON'u kopyalayıp programa ekleyin.

144 | 145 | Chrome Eklentisi Kurulumu:
146 | Chrome eklentisini manuel olarak yükleyin. Eklenti kurulduğunda, warp.dev/logged_in/remote sayfalarında otomatik hesap ekleme butonu görünür. Normal logged_in sayfalarında ise sayfa yenileme butonu belirir.

147 | 148 | Kullanım:
149 | Yazılım üzerine eklediğiniz hesapları kullanabilmek için Proxy\'yi etkinleştirirsiniz. Etkinleştirme işleminden sonra hesaplarınızdan birine başlat butonuna tıklayarak aktif edebilir ve Warp editörünü kullanmaya devam edebilirsiniz. "Limitleri Yenile" butonu ile hesaplarınız arasındaki limitleri anlık görebilirsiniz.''', 150 | 151 | # Sertifika kurulumu 152 | 'cert_title': '🔒 Proxy Sertifikası Kurulumu Gerekli', 153 | 'cert_explanation': '''Warp Proxy'nin düzgün çalışması için mitmproxy sertifikasının 154 | güvenilen kök sertifika yetkilileri arasına eklenmesi gerekiyor. 155 | 156 | Bu işlem sadece bir kez yapılır ve sistem güvenliğinizi etkilemez.''', 157 | 'cert_steps': '📋 Kurulum Adımları:', 158 | 'cert_step_1': 'Adım 1: Aşağıdaki "Sertifika Dosyasını Aç" butonuna tıklayın', 159 | 'cert_step_2': 'Adım 2: Açılan dosyaya çift tıklayın', 160 | 'cert_step_3': 'Adım 3: "Sertifika Yükle..." butonuna tıklayın', 161 | 'cert_step_4': 'Adım 4: "Yerel Makine" seçin ve "İleri" butonuna tıklayın', 162 | 'cert_step_5': 'Adım 5: "Tüm sertifikaları aşağıdaki depoya yerleştir" seçin', 163 | 'cert_step_6': 'Adım 6: "Gözat" butonuna tıklayın', 164 | 'cert_step_7': 'Adım 7: "Güvenilen Kök Sertifika Yetkilileri" klasörünü seçin', 165 | 'cert_step_8': 'Adım 8: "Tamam" ve "İleri" butonlarına tıklayın', 166 | 'cert_step_9': 'Adım 9: "Son" butonuna tıklayın', 167 | 'cert_path': 'Sertifika dosyası: {}', 168 | 169 | # Otomatik sertifika kurulumu 170 | 'cert_creating': '🔒 Sertifika oluşturuluyor...', 171 | 'cert_created_success': '✅ Sertifika dosyası başarıyla oluşturuldu', 172 | 'cert_creation_failed': '❌ Sertifika oluşturulamadı', 173 | 'cert_installing': '🔒 Sertifika kurulumu kontrol ediliyor...', 174 | 'cert_installed_success': '✅ Sertifika otomatik kuruldu', 175 | 'cert_install_failed': '❌ Sertifika kurulumu başarısız - Yönetici yetkisi gerekebilir', 176 | 'cert_install_error': '❌ Sertifika kurulum hatası: {}', 177 | 178 | # Manuel sertifika kurulum dialogu 179 | 'cert_manual_title': '🔒 Manuel Sertifika Kurulumu Gerekli', 180 | 'cert_manual_explanation': '''Otomatik sertifika kurulumu başarısız oldu. 181 | 182 | Sertifikayı manuel olarak kurmanız gerekiyor. Bu işlem sadece bir kez yapılır ve sistem güvenliğinizi etkilemez.''', 183 | 'cert_manual_path': 'Sertifika dosyası konumu:', 184 | 'cert_manual_steps': '''Manuel Kurulum Adımları:

185 | 1. Yukarıdaki dosya yoluna gidin
186 | 2. mitmproxy-ca-cert.cer dosyasına çift tıklayın
187 | 3. "Sertifika Yükle..." butonuna tıklayın
188 | 4. "Yerel Makine" seçin ve "İleri" tıklayın
189 | 5. "Tüm sertifikaları aşağıdaki depoya yerleştir" seçin
190 | 6. "Gözat" → "Güvenilen Kök Sertifika Yetkilileri" seçin
191 | 7. "Tamam" → "İleri" → "Son" tıklayın''', 192 | 'cert_open_folder': '📁 Sertifika Klasörünü Aç', 193 | 'cert_manual_complete': '✅ Kurulumu Tamamladım', 194 | 195 | # Mesajlar 196 | 'account_added_success': 'Hesap başarıyla eklendi', 197 | 'no_accounts_to_update': 'Güncellenecek hesap bulunamadı', 198 | 'updating_limits': 'Limitler güncelleniyor...', 199 | 'processing_account': 'İşleniyor: {}', 200 | 'refreshing_token': 'Token yenileniyor: {}', 201 | 'accounts_updated': '{} hesap güncellendi', 202 | 'proxy_starting': 'Proxy başlatılıyor...', 203 | 'proxy_configuring': 'Windows proxy ayarları yapılandırılıyor...', 204 | 'proxy_started': 'Proxy başlatıldı: {}', 205 | 'proxy_stopped': 'Proxy durduruldu', 206 | 'proxy_starting_account': 'Proxy başlatılıyor ve {} aktif ediliyor...', 207 | 'activating_account': 'Hesap aktif ediliyor: {}...', 208 | 'token_refreshing': 'Token yenileniyor: {}', 209 | 'proxy_started_account_activated': 'Proxy başlatıldı ve {} aktif edildi', 210 | 'windows_proxy_config_failed': 'Windows proxy ayarları yapılandırılamadı', 211 | 'mitmproxy_start_failed': 'Mitmproxy başlatılamadı - Port 8080 kontrol edin', 212 | 'proxy_start_error': 'Proxy başlatma hatası: {}', 213 | 'proxy_stop_error': 'Proxy durdurma hatası: {}', 214 | 'account_not_found': 'Hesap bulunamadı', 215 | 'account_banned_cannot_activate': '{} hesabı banlanmış - aktif edilemez', 216 | 'account_activation_error': 'Aktif etme hatası: {}', 217 | 'token_refresh_in_progress': 'Token yenileme devam ediyor, lütfen bekleyin...', 218 | 'token_refresh_error': 'Token yenileme hatası: {}', 219 | 'account_activated': '{} hesabı aktif edildi', 220 | 'account_activation_failed': 'Hesap aktif edilemedi', 221 | 'proxy_unexpected_stop': 'Proxy beklenmedik şekilde durduruldu', 222 | 'account_activated': '{} hesabı aktif edildi', 223 | 'account_deactivated': '{} hesabı deaktif edildi', 224 | 'account_deleted': '{} hesabı silindi', 225 | 'token_renewed': '{} tokeni yenilendi', 226 | 'account_banned_detected': '⛔ {} hesabı banlandı!', 227 | 'token_renewal_progress': '🔄 {}/{} token yenilendi', 228 | 229 | # Hata mesajları 230 | 'invalid_json': 'Geçersiz JSON formatı', 231 | 'email_not_found': 'Email bulunamadı', 232 | 'account_not_found': 'Hesap bulunamadı', 233 | 'certificate_not_found': 'Sertifika dosyası bulunamadı!', 234 | 'file_open_error': 'Dosya açma hatası: {}', 235 | 'proxy_start_failed': 'Proxy başlatılamadı - Port 8080 kontrol edin', 236 | 'proxy_config_failed': 'Windows proxy ayarları yapılandırılamadı', 237 | 'account_banned_cannot_activate': '{} hesabı banlanmış - aktif edilemez', 238 | 'token_refresh_failed': '{} tokeni yenilenemedi', 239 | 'account_delete_failed': 'Hesap silinemedi', 240 | 'proxy_unexpected_stop': '⚠️ Proxy beklenmedik şekilde durduruldu', 241 | 'enable_proxy_first': 'Hesap aktif etmek için önce proxy\'yi başlatın', 242 | 'limit_info_failed': 'Limit bilgisi alınamadı', 243 | 'token_renewal_failed': '⚠️ {} token yenilenemedi', 244 | 'token_check_error': '❌ Token kontrol hatası', 245 | 246 | # Onay mesajları 247 | 'delete_account_confirm': '\'{}\' hesabını silmek istediğinizden emin misiniz?\n\nBu işlem geri alınamaz!', 248 | 249 | # Durum çubuğu mesajları 250 | 'default_status': 'Proxy Etkinleştirip başlat butonuna tıklayarak kullanmaya başlayabilirsiniz.', 251 | 'default_status_debug': 'Proxy Etkinleştirip başlat butonuna tıklayarak kullanmaya başlayabilirsiniz. (Debug Modu Aktif)', 252 | 253 | # Debug ve konsol mesajları (bunlar değişmeyebilir ama tutarlılık için) 254 | 'stylesheet_load_error': 'Stil dosyası yüklenemedi: {}', 255 | 'health_update_error': 'Sağlık durumu güncelleme hatası: {}', 256 | 'token_update_error': 'Token güncelleme hatası: {}', 257 | 'account_update_error': 'Hesap güncelleme hatası: {}', 258 | 'active_account_set_error': 'Aktif hesap ayarlama hatası: {}', 259 | 'active_account_clear_error': 'Aktif hesap temizleme hatası: {}', 260 | 'account_delete_error': 'Hesap silme hatası: {}', 261 | 'limit_info_update_error': 'Limit bilgisi güncelleme hatası: {}', 262 | 263 | 264 | }, 265 | 266 | 'en': { 267 | # General 268 | 'app_title': 'Warp Account Manager', 269 | 'yes': 'Yes', 270 | 'no': 'No', 271 | 'ok': 'OK', 272 | 'cancel': 'Cancel', 273 | 'close': 'Close', 274 | 'error': 'Error', 275 | 'success': 'Success', 276 | 'warning': 'Warning', 277 | 'info': 'Info', 278 | 279 | # Buttons 280 | 'proxy_start': 'Start Proxy', 281 | 'proxy_stop': 'Stop Proxy', 282 | 'proxy_active': 'Proxy Active', 283 | 'add_account': 'Add Account', 284 | 'refresh_limits': 'Refresh Limits', 285 | 'help': 'Help', 286 | 'activate': '🟢 Activate', 287 | 'deactivate': '🔴 Deactivate', 288 | 'delete_account': '🗑️ Delete Account', 289 | 'create_account': '🌐 Create Account', 290 | 'add': 'Add', 291 | 'copy_javascript': '📋 Copy JavaScript Code', 292 | 'copied': '✅ Copied!', 293 | 'copy_error': '❌ Error!', 294 | 'open_certificate': '📁 Open Certificate File', 295 | 'installation_complete': '✅ Installation Complete', 296 | 297 | # Table headers 298 | 'current': 'Current', 299 | 'email': 'Email', 300 | 'status': 'Status', 301 | 'limit': 'Limit', 302 | 303 | # Activation button texts 304 | 'button_active': 'ACTIVE', 305 | 'button_inactive': 'INACTIVE', 306 | 'button_banned': 'BAN', 307 | 'button_start': 'Start', 308 | 'button_stop': 'Stop', 309 | 310 | # Status messages 311 | 'status_active': 'Active', 312 | 'status_banned': 'BAN', 313 | 'status_token_expired': 'Token Expired', 314 | 'status_proxy_active': ' (Proxy Active)', 315 | 'status_error': 'Error', 316 | 'status_na': 'N/A', 317 | 'status_not_updated': 'Not Updated', 318 | 'status_healthy': 'healthy', 319 | 'status_unhealthy': 'unhealthy', 320 | 'status_banned_key': 'banned', 321 | 322 | # Add account 323 | 'add_account_title': 'Add Account', 324 | 'add_account_instruction': 'Paste account JSON data below:', 325 | 'add_account_placeholder': 'Paste JSON data here...', 326 | 'how_to_get_json': '❓ How to get JSON data?', 327 | 'how_to_get_json_close': '❌ Close', 328 | 'json_info_title': 'How to Get JSON Data?', 329 | 330 | # Add account dialog tabs 331 | 'tab_manual': 'Manual', 332 | 'tab_auto': 'Automatic', 333 | 'manual_method_title': 'Manual JSON Addition', 334 | 'auto_method_title': 'Automatic Addition with Chrome Extension', 335 | 336 | # Chrome extension description 337 | 'chrome_extension_title': '🌐 Chrome Extension', 338 | 'chrome_extension_description': 'You can automatically add your accounts using our Chrome extension. This method is faster and easier.', 339 | 'chrome_extension_step_1': 'Step 1: Manually install the Chrome extension', 340 | 'chrome_extension_step_2': 'Step 2: Go to Warp.dev and create a new account', 341 | 'chrome_extension_step_3': 'Step 3: After creating account, click the extension button on the redirected page', 342 | 'chrome_extension_step_4': 'Step 4: Extension will automatically add the account to this program', 343 | 344 | # JSON extraction steps 345 | 'step_1': 'Step 1: Go to Warp website and login', 346 | 'step_2': 'Step 2: Open browser developer console (F12)', 347 | 'step_3': 'Step 3: Go to Console tab', 348 | 'step_4': 'Step 4: Paste the JavaScript code below into console', 349 | 'step_5': 'Step 5: Press Enter', 350 | 'step_6': 'Step 6: Click the button that appears on the page', 351 | 'step_7': 'Step 7: Paste the copied JSON here', 352 | 353 | # Help 354 | 'help_title': '📖 Warp Account Manager - User Guide', 355 | 'help_what_is': '🎯 What Does This Software Do?', 356 | 'help_what_is_content': 'You can view remaining limits between accounts you create to use Warp.dev code editor for free and easily switch between them by clicking the start button. It prevents you from getting banned by using different IDs for each operation.', 357 | 'help_how_works': '⚙️ How Does It Work?', 358 | 'help_how_works_content': 'It modifies requests made by Warp editor using proxy. It performs operations using the information of the account you selected and different user IDs.', 359 | 'help_how_to_use': '📝 How to Use?', 360 | 'help_how_to_use_content': '''Initial Setup:
361 | Since it works with proxy, you are expected to install the specified certificate in the trusted root certificate area on your computer at first launch. After completing the instructions, open Warp editor and login to any account. You must login to an account through the editor first.

362 | 363 | Adding Accounts (2 Methods):
364 | 1. Chrome Extension: Install our extension to Chrome. After creating account on Warp.dev, extension button appears on redirected page, one-click adds account automatically.
365 | 2. Manual Method: On account creation page, open console with F12, paste JavaScript code and copy JSON to add to program.

366 | 367 | Chrome Extension Installation:
368 | Manually install the Chrome extension. When extension is installed, automatic account addition button appears on warp.dev/logged_in/remote pages. On normal logged_in pages, a page refresh button appears.

369 | 370 | Usage:
371 | To use the accounts you added to the software, you activate the Proxy. After the activation process, you can activate one of your accounts by clicking the start button and continue using the Warp editor. You can instantly see the limits between your accounts with the "Refresh Limits" button.''', 372 | 373 | # Certificate installation 374 | 'cert_title': '🔒 Proxy Certificate Installation Required', 375 | 'cert_explanation': '''For Warp Proxy to work properly, mitmproxy certificate needs to be added to trusted root certificate authorities. 376 | 377 | This process is done only once and does not affect your system security.''', 378 | 'cert_steps': '📋 Installation Steps:', 379 | 'cert_step_1': 'Step 1: Click the "Open Certificate File" button below', 380 | 'cert_step_2': 'Step 2: Double-click the opened file', 381 | 'cert_step_3': 'Step 3: Click "Install Certificate..." button', 382 | 'cert_step_4': 'Step 4: Select "Local Machine" and click "Next"', 383 | 'cert_step_5': 'Step 5: Select "Place all certificates in the following store"', 384 | 'cert_step_6': 'Step 6: Click "Browse" button', 385 | 'cert_step_7': 'Step 7: Select "Trusted Root Certification Authorities" folder', 386 | 'cert_step_8': 'Step 8: Click "OK" and "Next" buttons', 387 | 'cert_step_9': 'Step 9: Click "Finish" button', 388 | 'cert_path': 'Certificate file: {}', 389 | 390 | # Automatic certificate installation 391 | 'cert_creating': '🔒 Creating certificate...', 392 | 'cert_created_success': '✅ Certificate file created successfully', 393 | 'cert_creation_failed': '❌ Certificate creation failed', 394 | 'cert_installing': '🔒 Checking certificate installation...', 395 | 'cert_installed_success': '✅ Certificate installed automatically', 396 | 'cert_install_failed': '❌ Certificate installation failed - Administrator privileges may be required', 397 | 'cert_install_error': '❌ Certificate installation error: {}', 398 | 399 | # Manual certificate installation dialog 400 | 'cert_manual_title': '🔒 Manual Certificate Installation Required', 401 | 'cert_manual_explanation': '''Automatic certificate installation failed. 402 | 403 | You need to install the certificate manually. This process is done only once and does not affect your system security.''', 404 | 'cert_manual_path': 'Certificate file location:', 405 | 'cert_manual_steps': '''Manual Installation Steps:

406 | 1. Go to the file path above
407 | 2. Double-click the mitmproxy-ca-cert.cer file
408 | 3. Click "Install Certificate..." button
409 | 4. Select "Local Machine" and click "Next"
410 | 5. Select "Place all certificates in the following store"
411 | 6. Click "Browse" → Select "Trusted Root Certification Authorities"
412 | 7. Click "OK" → "Next" → "Finish"''', 413 | 'cert_open_folder': '📁 Open Certificate Folder', 414 | 'cert_manual_complete': '✅ Installation Complete', 415 | 416 | # Messages 417 | 'account_added_success': 'Account added successfully', 418 | 'no_accounts_to_update': 'No accounts found to update', 419 | 'updating_limits': 'Updating limits...', 420 | 'processing_account': 'Processing: {}', 421 | 'refreshing_token': 'Refreshing token: {}', 422 | 'accounts_updated': '{} accounts updated', 423 | 'proxy_starting': 'Starting proxy...', 424 | 'proxy_configuring': 'Configuring Windows proxy settings...', 425 | 'proxy_started': 'Proxy started: {}', 426 | 'proxy_stopped': 'Proxy stopped', 427 | 'proxy_starting_account': 'Starting proxy and activating {}...', 428 | 'activating_account': 'Activating account: {}...', 429 | 'token_refreshing': 'Refreshing token: {}', 430 | 'proxy_started_account_activated': 'Proxy started and {} activated', 431 | 'windows_proxy_config_failed': 'Windows proxy configuration failed', 432 | 'mitmproxy_start_failed': 'Mitmproxy failed to start - Check port 8080', 433 | 'proxy_start_error': 'Proxy start error: {}', 434 | 'proxy_stop_error': 'Proxy stop error: {}', 435 | 'account_not_found': 'Account not found', 436 | 'account_banned_cannot_activate': '{} account is banned - cannot activate', 437 | 'account_activation_error': 'Account activation error: {}', 438 | 'token_refresh_in_progress': 'Token refresh in progress, please wait...', 439 | 'token_refresh_error': 'Token refresh error: {}', 440 | 'account_activated': '{} account activated', 441 | 'account_activation_failed': 'Account activation failed', 442 | 'proxy_unexpected_stop': 'Proxy stopped unexpectedly', 443 | 'account_activated': '{} account activated', 444 | 'account_deactivated': '{} account deactivated', 445 | 'account_deleted': '{} account deleted', 446 | 'token_renewed': '{} token renewed', 447 | 'account_banned_detected': '⛔ {} account banned!', 448 | 'token_renewal_progress': '🔄 {}/{} tokens renewed', 449 | 450 | # Error messages 451 | 'invalid_json': 'Invalid JSON format', 452 | 'email_not_found': 'Email not found', 453 | 'account_not_found': 'Account not found', 454 | 'certificate_not_found': 'Certificate file not found!', 455 | 'file_open_error': 'File open error: {}', 456 | 'proxy_start_failed': 'Proxy could not be started - Check port 8080', 457 | 'proxy_config_failed': 'Windows proxy settings could not be configured', 458 | 'account_banned_cannot_activate': '{} account is banned - cannot be activated', 459 | 'token_refresh_failed': '{} token could not be renewed', 460 | 'account_delete_failed': 'Account could not be deleted', 461 | 'proxy_unexpected_stop': '⚠️ Proxy stopped unexpectedly', 462 | 'enable_proxy_first': 'Start proxy first to activate account', 463 | 'limit_info_failed': 'Could not get limit information', 464 | 'token_renewal_failed': '⚠️ {} token could not be renewed', 465 | 'token_check_error': '❌ Token check error', 466 | 467 | # Confirmation messages 468 | 'delete_account_confirm': 'Are you sure you want to delete \'{}\' account?\n\nThis action cannot be undone!', 469 | 470 | # Status bar messages 471 | 'default_status': 'Enable Proxy and click the start button on accounts to start using.', 472 | 'default_status_debug': 'Enable Proxy and click the start button on accounts to start using. (Debug Mode Active)', 473 | 474 | # Debug and console messages (these might not change but for consistency) 475 | 'stylesheet_load_error': 'Could not load stylesheet: {}', 476 | 'health_update_error': 'Health status update error: {}', 477 | 'token_update_error': 'Token update error: {}', 478 | 'account_update_error': 'Account update error: {}', 479 | 'active_account_set_error': 'Active account set error: {}', 480 | 'active_account_clear_error': 'Active account clear error: {}', 481 | 'account_delete_error': 'Account delete error: {}', 482 | 'limit_info_update_error': 'Limit info update error: {}', 483 | 484 | 485 | } 486 | } 487 | 488 | return translations 489 | 490 | def get_text(self, key, *args): 491 | """Çeviri metnini al""" 492 | try: 493 | text = self.translations[self.current_language].get(key, key) 494 | if args: 495 | return text.format(*args) 496 | return text 497 | except: 498 | return key 499 | 500 | def set_language(self, language_code): 501 | """Dili değiştir""" 502 | if language_code in self.translations: 503 | self.current_language = language_code 504 | return True 505 | return False 506 | 507 | def get_current_language(self): 508 | """Mevcut dili döndür""" 509 | return self.current_language 510 | 511 | def get_available_languages(self): 512 | """Kullanılabilir dilleri döndür""" 513 | return list(self.translations.keys()) 514 | 515 | # Global dil yöneticisi instance'ı 516 | _language_manager = None 517 | 518 | def get_language_manager(): 519 | """Global dil yöneticisini al""" 520 | global _language_manager 521 | if _language_manager is None: 522 | _language_manager = LanguageManager() 523 | return _language_manager 524 | 525 | def _(key, *args): 526 | """Kısa çeviri fonksiyonu""" 527 | return get_language_manager().get_text(key, *args) 528 | --------------------------------------------------------------------------------