import threading import time import os import random import requests from flask import Flask import scratchattach as sa # ==================== CONFIGURATION ==================== PROJECT_ID = "1298553820" # Your Scratch project ID SCRATCH_USER = os.environ.get("SCRATCH_USER") SCRATCH_PASS = os.environ.get("SCRATCH_PASS") AI_URL = "https://16dvnk-scratch-ai.hf.space/generate" # Updated AI endpoint # ==================== PROXY LIST (same as before) ==================== PROXY_LIST = [ "http://8.219.97.248:80", "http://38.158.83.233:999", "http://102.68.128.216:8080", "http://8.243.126.136:8801", "http://45.236.66.163:8520", "http://103.155.167.62:8080", "http://119.92.71.40:8080", "http://103.144.146.5:8080", "http://36.91.220.132:8080", "http://41.254.48.192:1976" ] # ==================== ENCODING / DECODING ==================== def encode_for_scratch(text): encoded = [] for ch in text[:250]: if 'a' <= ch <= 'z': code = ord(ch) - ord('a') + 1 encoded.append(str(code).zfill(2)) elif 'A' <= ch <= 'Z': code = ord(ch) - ord('A') + 28 # Uppercase range encoded.append(str(code).zfill(2)) elif ch == ' ': encoded.append("27") else: encoded.append("99") return "".join(encoded) def decode_from_scratch(encoded): result = [] i = 0 while i < len(encoded): if i + 1 < len(encoded) and encoded[i:i+2].isdigit(): num = int(encoded[i:i+2]) if 1 <= num <= 26: result.append(chr(num + 96)) # lowercase elif 28 <= num <= 53: result.append(chr(num - 28 + 65)) # uppercase elif num == 27: result.append(' ') elif num == 99: result.append('?') else: result.append('?') i += 2 else: result.append('?') i += 1 return "".join(result) # ==================== PROXY ROTATOR ==================== class ProxyRotator: def __init__(self, proxies): self.proxies = proxies self.current_index = 0 self.failed_proxies = set() def get_next_proxy(self): if len(self.failed_proxies) >= len(self.proxies): self.failed_proxies.clear() attempts = 0 while attempts < len(self.proxies): proxy = self.proxies[self.current_index % len(self.proxies)] self.current_index += 1 if proxy not in self.failed_proxies: return {"http": proxy, "https": proxy} self.failed_proxies.clear() proxy = self.proxies[self.current_index % len(self.proxies)] self.current_index += 1 return {"http": proxy, "https": proxy} def mark_proxy_failed(self, proxy_url): if isinstance(proxy_url, dict): proxy_url = proxy_url.get("http", "") self.failed_proxies.add(proxy_url) proxy_rotator = ProxyRotator(PROXY_LIST) # ==================== AI CALL WITH PROXY ROTATION ==================== def call_ai(prompt): print(f"[AI] Sending prompt: {prompt[:50]}...") for attempt in range(min(5, len(PROXY_LIST))): proxy = proxy_rotator.get_next_proxy() proxy_url = proxy.get("http", "") try: response = requests.post(AI_URL, json={"prompt": prompt}, proxies=proxy, timeout=5) if response.status_code == 200: data = response.json() # Try common response keys result = data.get("response") or data.get("text") or data.get("output") or "(no reply)" print(f"[AI] Success (proxy {proxy_url})") return result else: print(f"[AI] Proxy {proxy_url} returned {response.status_code}") proxy_rotator.mark_proxy_failed(proxy) except Exception as e: print(f"[AI] Proxy {proxy_url} error: {e}") proxy_rotator.mark_proxy_failed(proxy) # Fallback direct try: response = requests.post(AI_URL, json={"prompt": prompt}, timeout=5) if response.status_code == 200: data = response.json() result = data.get("response") or data.get("text") or data.get("output") or "(no reply)" return result except Exception as e: print(f"[AI] Direct call error: {e}") return "Error: Could not reach AI service." ai_text = call_ai(user_prompt) print(f"[DEBUG] Raw AI response: {ai_text}") # <-- add this # ==================== BRIDGE WORKER ==================== def bridge_worker(): print("[BRIDGE] Starting bridge with corrected encoding/decoding") print(f"[BRIDGE] Project ID: {PROJECT_ID}") if not SCRATCH_USER or not SCRATCH_PASS: print("[BRIDGE] ERROR: SCRATCH_USER and SCRATCH_PASS environment variables missing") return while True: try: print(f"[BRIDGE] Logging in as {SCRATCH_USER}...") session = sa.login(SCRATCH_USER, SCRATCH_PASS) print("[BRIDGE] Login successful. Connecting to cloud...") cloud = session.connect_cloud(PROJECT_ID) print(f"[BRIDGE] Connected to project {PROJECT_ID}") # Set up event listener for cloud variable changes events = cloud.events() print("[BRIDGE] Event listener active. Waiting for 'prompt' changes...") @events.event def on_set(activity): if activity.var == "prompt" and activity.value != "0": print(f"[EVENT] Received encoded prompt: {activity.value}") # Decode the prompt (from Scratch format) user_prompt = decode_from_scratch(activity.value) print(f"[EVENT] Decoded prompt: {user_prompt}") # Get AI response ai_text = call_ai(user_prompt) print(f"[EVENT] AI response: {ai_text[:100]}...") # Encode the AI response for Scratch encoded_response = encode_for_scratch(ai_text) # Send back to Scratch cloud.set_var("response", encoded_response) print("[EVENT] Response sent to Scratch") events.start() # Keep the thread alive while True: time.sleep(0.3) except Exception as e: print(f"[BRIDGE] Error: {e}") import traceback traceback.print_exc() print("[BRIDGE] Reconnecting in 0.3 seconds...") time.sleep(0.3) # ==================== FLASK APP ==================== app = Flask(__name__) @app.route('/') def health(): return "Scratch AI Bridge is running (fixed encoding)." if __name__ == '__main__': print("[MAIN] Starting bridge thread...") thread = threading.Thread(target=bridge_worker, daemon=True) thread.start() print("[MAIN] Starting Flask server...") app.run(host='0.0.0.0', port=7860)