| import threading |
| import time |
| import os |
| import random |
| import requests |
| from flask import Flask |
| import scratchattach as sa |
|
|
| |
| PROJECT_ID = "1298553820" |
| SCRATCH_USER = os.environ.get("SCRATCH_USER") |
| SCRATCH_PASS = os.environ.get("SCRATCH_PASS") |
| AI_URL = "https://16dvnk-scratch-ai.hf.space/generate" |
|
|
| |
| 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" |
| ] |
|
|
| |
| 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 |
| 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)) |
| elif 28 <= num <= 53: |
| result.append(chr(num - 28 + 65)) |
| elif num == 27: |
| result.append(' ') |
| elif num == 99: |
| result.append('?') |
| else: |
| result.append('?') |
| i += 2 |
| else: |
| result.append('?') |
| i += 1 |
| return "".join(result) |
|
|
| |
| 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) |
|
|
| |
| 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() |
| |
| 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) |
| |
| 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}") |
|
|
| |
| 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}") |
| |
| |
| 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}") |
| |
| user_prompt = decode_from_scratch(activity.value) |
| print(f"[EVENT] Decoded prompt: {user_prompt}") |
| |
| ai_text = call_ai(user_prompt) |
| print(f"[EVENT] AI response: {ai_text[:100]}...") |
| |
| encoded_response = encode_for_scratch(ai_text) |
| |
| cloud.set_var("response", encoded_response) |
| print("[EVENT] Response sent to Scratch") |
| |
| events.start() |
| |
| |
| 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) |
|
|
| |
| 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) |