/ tor_api_setup.py
tor_api_setup.py
1 import sys 2 import os 3 import subprocess 4 import requests 5 import socket 6 import time 7 import re 8 from PyQt6.QtCore import QThread, pyqtSignal 9 10 class TORAPISetup(QThread): 11 """Thread to handle API setup through TOR with robust error handling""" 12 output_signal = pyqtSignal(str) 13 finished_signal = pyqtSignal(bool) 14 15 def __init__(self, tor_port=9050, control_port=9051, tor_password=None): 16 super().__init__() 17 self.tor_port = tor_port 18 self.control_port = control_port 19 self.tor_password = tor_password 20 self.process = None 21 self.tor_session = None 22 23 def create_tor_session(self): 24 """Create a TOR session with proper configuration""" 25 session = requests.Session() 26 session.proxies = { 27 'http': f'socks5h://127.0.0.1:{self.tor_port}', 28 'https': f'socks5h://127.0.0.1:{self.tor_port}' 29 } 30 31 # Add headers to avoid detection 32 session.headers.update({ 33 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 34 'Accept': 'application/json, text/plain, */*', 35 'Accept-Language': 'en-US,en;q=0.9', 36 'Accept-Encoding': 'gzip, deflate, br', 37 'Connection': 'keep-alive', 38 }) 39 40 session.timeout = 15 41 return session 42 43 def check_tor_connection(self, retries=3): 44 """Check TOR connection with multiple endpoints and retries""" 45 if not self.tor_session: 46 self.tor_session = self.create_tor_session() 47 48 test_urls = [ 49 ("https://httpbin.org/ip", "httpbin"), 50 ("https://api.ipify.org?format=json", "ipify"), 51 ("https://icanhazip.com", "icanhazip"), 52 ("https://checkip.amazonaws.com", "aws"), 53 ("https://ipinfo.io/ip", "ipinfo"), 54 ("http://checkip.dyndns.org", "dyndns"), 55 ] 56 57 successful_tests = 0 58 last_ip = None 59 60 for url, service_name in test_urls: 61 for attempt in range(retries): 62 try: 63 self.output_signal.emit(f"đ Testing TOR via {service_name}...") 64 response = self.tor_session.get(url, timeout=10) 65 66 if response.status_code == 200: 67 # Extract IP from response 68 try: 69 if 'application/json' in response.headers.get('Content-Type', ''): 70 ip_data = response.json() 71 ip = ip_data.get("origin") or ip_data.get("ip") or ip_data.get("query") 72 else: 73 # Parse plain text 74 text = response.text.strip() 75 ip_match = re.search(r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b', text) 76 ip = ip_match.group(0) if ip_match else text 77 78 if ip: 79 if last_ip is None: 80 last_ip = ip 81 82 # Check if IPs are consistent (should be same TOR exit node) 83 if ip == last_ip: 84 self.output_signal.emit(f"â {service_name}: IP {ip}") 85 successful_tests += 1 86 else: 87 self.output_signal.emit(f"â ī¸ {service_name}: Different IP {ip} (expected {last_ip})") 88 # Still count as successful if we got an IP 89 successful_tests += 1 90 last_ip = ip 91 92 # If we have at least 2 successful tests, TOR is working 93 if successful_tests >= 2: 94 self.output_signal.emit(f"đ TOR connection established - Current IP: {last_ip}") 95 return True 96 97 break # Success, move to next endpoint 98 except Exception as e: 99 self.output_signal.emit(f"â ī¸ Could not parse {service_name} response: {e}") 100 else: 101 self.output_signal.emit(f"â ī¸ {service_name}: HTTP {response.status_code}") 102 103 except requests.exceptions.Timeout: 104 self.output_signal.emit(f"â ī¸ {service_name}: Timeout (attempt {attempt + 1}/{retries})") 105 if attempt < retries - 1: 106 time.sleep(2) 107 except requests.exceptions.ConnectionError: 108 self.output_signal.emit(f"â ī¸ {service_name}: Connection error (attempt {attempt + 1}/{retries})") 109 if attempt < retries - 1: 110 time.sleep(2) 111 except Exception as e: 112 self.output_signal.emit(f"â ī¸ {service_name}: Error {e}") 113 if attempt < retries - 1: 114 time.sleep(2) 115 116 # Small delay between endpoints 117 time.sleep(0.5) 118 119 if successful_tests > 0: 120 self.output_signal.emit(f"â ī¸ Partial TOR connectivity: {successful_tests}/6 endpoints") 121 return True 122 123 self.output_signal.emit("â TOR connection failed across all endpoints") 124 return False 125 126 def renew_tor_ip(self): 127 """Get fresh TOR IP with robust error handling""" 128 try: 129 self.output_signal.emit("đ Renewing TOR IP address...") 130 131 # Connect to TOR control port 132 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 133 sock.settimeout(15) 134 sock.connect(('127.0.0.1', self.control_port)) 135 136 # Authenticate 137 if self.tor_password: 138 auth_cmd = f'AUTHENTICATE "{self.tor_password}"\r\n' 139 sock.send(auth_cmd.encode()) 140 response = sock.recv(1024).decode() 141 142 if '250 OK' in response: 143 self.output_signal.emit("đ Authenticated with password") 144 else: 145 # Try without password 146 auth_cmd = 'AUTHENTICATE\r\n' 147 sock.send(auth_cmd.encode()) 148 response = sock.recv(1024).decode() 149 else: 150 auth_cmd = 'AUTHENTICATE\r\n' 151 sock.send(auth_cmd.encode()) 152 response = sock.recv(1024).decode() 153 154 if '250 OK' not in response: 155 self.output_signal.emit(f"â Authentication failed: {response}") 156 sock.close() 157 return False 158 159 # Request new circuit 160 sock.send(b'SIGNAL NEWNYM\r\n') 161 response = sock.recv(1024).decode() 162 163 if '250 OK' not in response: 164 self.output_signal.emit(f"â NEWNYM failed: {response}") 165 sock.close() 166 return False 167 168 sock.send(b'QUIT\r\n') 169 sock.close() 170 171 self.output_signal.emit("â TOR circuit renewed") 172 173 # Wait for circuit to establish and verify 174 time.sleep(5) 175 176 # Verify new IP 177 if self.check_tor_connection(retries=2): 178 self.output_signal.emit("â Fresh TOR IP ready for API registration") 179 return True 180 else: 181 self.output_signal.emit("â ī¸ TOR renewal succeeded but IP verification failed") 182 return True # Still continue with setup 183 184 except socket.timeout: 185 self.output_signal.emit("â TOR control port timeout - is TOR running?") 186 return False 187 except ConnectionRefusedError: 188 self.output_signal.emit("â TOR control port refused - check TOR configuration") 189 return False 190 except Exception as e: 191 self.output_signal.emit(f"â TOR renewal error: {e}") 192 # Don't fail entirely, continue with current IP 193 return True 194 195 def verify_api_connectivity(self): 196 """Verify connectivity to Blackbird AI API through TOR""" 197 test_endpoints = [ 198 "https://api.blackbird.ai/status", # If there's a status endpoint 199 "https://api.blackbird.ai/", # Base API endpoint 200 ] 201 202 for endpoint in test_endpoints: 203 try: 204 response = self.tor_session.get(endpoint, timeout=10) 205 if response.status_code < 500: # Anything other than server errors 206 self.output_signal.emit(f"â API connectivity test passed: {endpoint}") 207 return True 208 else: 209 self.output_signal.emit(f"â ī¸ API endpoint {endpoint}: HTTP {response.status_code}") 210 except Exception as e: 211 self.output_signal.emit(f"â ī¸ API endpoint {endpoint}: {e}") 212 213 self.output_signal.emit("â ī¸ API connectivity tests inconclusive - proceeding anyway") 214 return True # Proceed even if we can't reach API 215 216 def run_blackbird_setup(self): 217 """Run blackbird setup command with proper environment""" 218 try: 219 # Set up environment variables for TOR 220 env = os.environ.copy() 221 env['ALL_PROXY'] = f'socks5h://127.0.0.1:{self.tor_port}' 222 env['HTTP_PROXY'] = f'socks5h://127.0.0.1:{self.tor_port}' 223 env['HTTPS_PROXY'] = f'socks5h://127.0.0.1:{self.tor_port}' 224 env['BLACKBIRD_USE_TOR'] = '1' # Signal to blackbird.py to use TOR 225 226 # Run the setup command 227 cmd = ['python', 'blackbird.py', '--setup-ai'] 228 self.output_signal.emit(f"đ Starting: {' '.join(cmd)}") 229 230 self.process = subprocess.Popen( 231 cmd, 232 stdout=subprocess.PIPE, 233 stderr=subprocess.STDOUT, 234 stdin=subprocess.PIPE, 235 text=True, 236 env=env, 237 bufsize=1, 238 universal_newlines=True 239 ) 240 241 # Monitor output and handle prompts 242 confirmation_sent = False 243 for line in iter(self.process.stdout.readline, ''): 244 if not line: 245 break 246 247 text = line.strip() 248 self.output_signal.emit(text) 249 250 # Look for registration prompt 251 if not confirmation_sent: 252 if any(prompt in text.lower() for prompt in ['ip is registered', '[y/n]', 'register this ip', 'confirm']): 253 try: 254 # Send confirmation 255 self.process.stdin.write('Y\n') 256 self.process.stdin.flush() 257 confirmation_sent = True 258 self.output_signal.emit("â Sent confirmation for TOR IP registration") 259 except Exception as e: 260 self.output_signal.emit(f"â ī¸ Could not send confirmation: {e}") 261 262 # Check for success indicators 263 if any(success in text.lower() for success in ['success', 'api key saved', 'registered', 'setup complete']): 264 self.output_signal.emit("â API setup appears successful") 265 266 # Wait for process to complete 267 return_code = self.process.wait() 268 269 if return_code == 0: 270 self.output_signal.emit("â Blackbird AI setup completed successfully") 271 return True 272 else: 273 self.output_signal.emit(f"â ī¸ Blackbird setup returned code {return_code}") 274 # Still might be successful if API key was saved 275 return self.check_api_key_saved() 276 277 except Exception as e: 278 self.output_signal.emit(f"â Setup process failed: {e}") 279 return False 280 281 def check_api_key_saved(self): 282 """Check if API key was saved to file""" 283 config_paths = [ 284 os.path.expanduser("~/.ai_key.json"), 285 ".ai_key.json", 286 "ai_key.json" 287 ] 288 289 for config_path in config_paths: 290 if os.path.exists(config_path): 291 try: 292 with open(config_path, 'r') as f: 293 import json 294 config = json.load(f) 295 if config.get("ai_api_key") or config.get("api_key"): 296 self.output_signal.emit(f"â API key found in {config_path}") 297 return True 298 except Exception as e: 299 self.output_signal.emit(f"â ī¸ Could not read {config_path}: {e}") 300 301 self.output_signal.emit("â ī¸ No API key found in expected locations") 302 return False 303 304 def run(self): 305 """Execute API setup through TOR with comprehensive error handling""" 306 self.output_signal.emit("=" * 60) 307 self.output_signal.emit("đ BLACKBIRD AI API SETUP THROUGH TOR") 308 self.output_signal.emit("=" * 60) 309 310 # Step 1: Establish TOR connection 311 self.output_signal.emit("đ§ Step 1: Testing TOR connectivity...") 312 if not self.check_tor_connection(): 313 self.output_signal.emit("â Cannot proceed without TOR connection") 314 self.finished_signal.emit(False) 315 return 316 317 # Step 2: Renew IP for fresh registration 318 self.output_signal.emit("đ§ Step 2: Getting fresh TOR IP for registration...") 319 if not self.renew_tor_ip(): 320 self.output_signal.emit("â ī¸ IP renewal failed, continuing with current IP...") 321 322 # Step 3: Verify API connectivity 323 self.output_signal.emit("đ§ Step 3: Testing API connectivity...") 324 self.verify_api_connectivity() 325 326 # Step 4: Run the setup 327 self.output_signal.emit("đ§ Step 4: Running Blackbird AI setup...") 328 success = self.run_blackbird_setup() 329 330 # Step 5: Final verification 331 if success: 332 self.output_signal.emit("đ§ Step 5: Verifying setup...") 333 if self.check_api_key_saved(): 334 self.output_signal.emit("=" * 60) 335 self.output_signal.emit("â API SETUP COMPLETE THROUGH TOR") 336 self.output_signal.emit("=" * 60) 337 self.finished_signal.emit(True) 338 return 339 340 self.output_signal.emit("=" * 60) 341 self.output_signal.emit("â ī¸ API SETUP MAY NOT HAVE COMPLETED SUCCESSFULLY") 342 self.output_signal.emit("=" * 60) 343 self.finished_signal.emit(False) 344 345 def stop(self): 346 """Stop the setup process""" 347 if self.process and self.process.poll() is None: 348 self.process.terminate() 349 try: 350 self.process.wait(timeout=5) 351 except subprocess.TimeoutExpired: 352 self.process.kill() 353 self.process.wait() 354 self.output_signal.emit("âšī¸ Setup process stopped")