File size: 7,169 Bytes
d65c869
 
 
0b536a4
3b4cd75
d604648
2e56a78
d65c869
3b4cd75
12a915d
df9b297
 
12a915d
cf5e82d
12a915d
0b536a4
 
 
 
 
 
 
 
 
 
e649e62
0b536a4
 
12a915d
 
d65c869
d585e26
 
 
 
 
 
12a915d
d65c869
1d5598d
d65c869
d585e26
d65c869
 
12a915d
d65c869
 
 
12a915d
d65c869
 
d585e26
 
 
5103788
d1227cc
12a915d
 
d1227cc
 
d65c869
 
 
 
 
 
0b536a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12a915d
9cdbb21
12a915d
cf5e82d
0b536a4
2c514b7
0b536a4
e649e62
0b536a4
12a915d
 
 
 
2c514b7
0b536a4
12a915d
0b536a4
 
12a915d
0b536a4
cf5e82d
9cdbb21
e649e62
9cdbb21
12a915d
 
 
2c514b7
 
0b536a4
 
9181bd5
 
 
1ccae7f
d65c869
12a915d
4d5e864
2e56a78
df9b297
12a915d
2e56a78
0b536a4
3b4cd75
d65c869
cf5e82d
 
 
 
 
2e56a78
12a915d
cf5e82d
 
 
 
 
 
12a915d
 
 
 
 
 
 
 
 
 
cf5e82d
 
27687e2
cf5e82d
27687e2
cf5e82d
 
308ddeb
cf5e82d
d65c869
cf5e82d
2c514b7
 
308ddeb
 
d65c869
3b4cd75
d604648
d65c869
d604648
 
12a915d
d65c869
d604648
d1227cc
d604648
 
d1227cc
d604648
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
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)