scratch / app.py
16dvnk's picture
Update app.py
9181bd5 verified
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)