16dvnk commited on
Commit
80ceb4e
·
verified ·
1 Parent(s): 3a0e1bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -140
app.py CHANGED
@@ -3,94 +3,22 @@ import time
3
  import os
4
  import random
5
  import requests
 
6
  from flask import Flask
7
- import scratchconnect
 
8
 
9
  # ==================== CONFIGURATION ====================
10
- # Read environment variables set in Hugging Face Secrets
11
  SCRATCH_USER = os.environ.get("SCRATCH_USER")
12
  SCRATCH_PASS = os.environ.get("SCRATCH_PASS")
13
  PROJECT_ID = int(os.environ.get("PROJECT_ID", 0))
14
  AI_URL = "https://16dvnk-spirit-kings-ai.hf.space/generate"
15
 
16
- # ==================== PROXY LIST ====================
17
- PROXY_LIST = [
18
- "http://8.219.97.248:80",
19
- "http://38.158.83.233:999",
20
- "http://102.68.128.216:8080",
21
- "http://8.243.126.136:8801",
22
- "http://45.236.66.163:8520",
23
- "http://103.155.167.62:8080",
24
- "http://119.92.71.40:8080",
25
- "http://103.144.146.5:8080",
26
- "http://36.91.220.132:8080",
27
- "http://41.254.48.192:1976",
28
- "http://45.169.169.14:8085",
29
- "http://103.3.246.71:3128",
30
- "http://185.225.40.236:8080",
31
- "http://164.90.151.28:3128",
32
- "http://177.234.211.31:999",
33
- "http://38.156.73.61:8080",
34
- "http://103.68.214.135:8181",
35
- "http://169.255.77.150:9999",
36
- "http://35.225.22.61:80",
37
- "http://103.125.174.233:7777",
38
- "http://103.171.83.115:7777",
39
- "http://84.247.149.172:3128",
40
- "http://51.79.135.131:8080",
41
- "http://186.148.184.18:999",
42
- "http://116.80.64.157:7777",
43
- "http://116.80.64.158:7777",
44
- "http://116.80.48.217:7777",
45
- "http://116.0.54.25:8080",
46
- "http://116.80.95.238:7777",
47
- "http://142.171.157.207:3128",
48
- "http://154.119.80.27:3128",
49
- "http://103.82.93.98:3128",
50
- "http://104.248.81.109:3128",
51
- "http://194.163.183.242:3128",
52
- "http://49.144.17.15:8082",
53
- "http://103.170.22.44:8080",
54
- "http://160.22.234.4:1111",
55
- "http://116.80.49.156:3172",
56
- "http://116.80.49.163:3172",
57
- "http://116.80.96.95:3172",
58
- "http://116.80.96.100:3172",
59
- "http://116.80.96.107:3172",
60
- "http://116.80.65.77:3172",
61
- "http://45.136.198.40:3128",
62
- "http://130.61.139.145:3128",
63
- "http://116.80.63.64:7777",
64
- "http://150.95.26.146:7080",
65
- "http://116.80.96.103:3172",
66
- "http://20.2.83.243:3128",
67
- "http://165.232.146.249:3128",
68
- "http://199.68.217.2:3128",
69
- "http://116.80.65.75:3172",
70
- "http://116.80.65.78:3172",
71
- "http://116.80.65.81:3172",
72
- "http://116.80.65.85:3172",
73
- "http://45.119.85.216:3128",
74
- "http://192.232.48.2:8181",
75
- "http://116.80.65.82:3172",
76
- "http://95.213.217.168:52004",
77
- "http://174.140.109.250:3128",
78
- "http://185.118.51.163:3128",
79
- "http://46.175.148.17:2040",
80
- "http://42.96.16.158:1311",
81
- "http://103.30.31.209:32323",
82
- "http://45.131.6.46:80",
83
- "http://172.67.70.113:80",
84
- "http://103.21.244.88:80",
85
- "http://103.21.244.43:80",
86
- "http://103.21.244.189:80",
87
- "http://103.21.244.186:80",
88
- "http://103.21.244.165:80",
89
- "http://103.21.244.150:80",
90
- "http://103.21.244.210:80",
91
- "http://173.245.49.114:80",
92
- "http://103.21.244.12:80",
93
- "http://178.130.47.129:1082"
94
  ]
95
 
96
  # ==================== ENCODING/DECODING ====================
@@ -123,81 +51,81 @@ def decode_text(encoded):
123
  i += 1
124
  return "".join(result)
125
 
126
- # ==================== PROXY ROTATOR ====================
127
- class ProxyRotator:
128
- def __init__(self, proxies):
129
- self.proxies = proxies
130
- self.current_index = 0
131
- self.failed_proxies = set()
132
-
133
- def get_next_proxy(self):
134
- if len(self.failed_proxies) >= len(self.proxies):
135
- self.failed_proxies.clear()
136
- attempts = 0
137
- while attempts < len(self.proxies):
138
- proxy = self.proxies[self.current_index % len(self.proxies)]
139
- self.current_index += 1
140
- if proxy not in self.failed_proxies:
141
- return {"http": proxy, "https": proxy}
142
- self.failed_proxies.clear()
143
- proxy = self.proxies[self.current_index % len(self.proxies)]
144
- self.current_index += 1
145
- return {"http": proxy, "https": proxy}
146
-
147
- def mark_proxy_failed(self, proxy_url):
148
- if isinstance(proxy_url, dict):
149
- proxy_url = proxy_url.get("http", "")
150
- self.failed_proxies.add(proxy_url)
151
-
152
- proxy_rotator = ProxyRotator(PROXY_LIST)
153
-
154
- # ==================== AI CALL WITH PROXY ROTATION ====================
155
- def call_ai(prompt):
156
- for attempt in range(len(PROXY_LIST)):
157
- proxy = proxy_rotator.get_next_proxy()
158
- try:
159
- response = requests.post(AI_URL, json={"prompt": prompt}, proxies=proxy, timeout=30)
160
- if response.status_code == 200:
161
- return response.json().get("response", "Sorry, no response.")
162
- else:
163
- proxy_rotator.mark_proxy_failed(proxy)
164
- except Exception as e:
165
- proxy_rotator.mark_proxy_failed(proxy)
166
- try:
167
- response = requests.post(AI_URL, json={"prompt": prompt}, timeout=30)
168
- if response.status_code == 200:
169
- return response.json().get("response", "Sorry, no response.")
170
- except:
171
- pass
172
- return "Error: Could not reach AI service."
173
 
174
  # ==================== BRIDGE WORKER ====================
175
  def bridge_worker():
176
- print("[BRIDGE] Starting bridge with proxy rotation...")
177
-
 
178
  while True:
179
  try:
180
  print("[BRIDGE] Logging into Scratch...")
181
- user = scratchconnect.ScratchConnect(SCRATCH_USER, SCRATCH_PASS)
182
- print("[BRIDGE] Login successful. Connecting to project...")
183
- project = user.connect_project(project_id=PROJECT_ID)
184
- print("[BRIDGE] Project connected. Connecting to cloud variables...")
185
- cloud = project.connect_cloud_variables()
186
- print("[BRIDGE] Cloud connection established. Waiting for prompts...")
187
 
188
  last_prompt = ""
189
  while True:
190
- prompt_history = cloud.get_cloud_variable_value(variable_name="prompt")
191
- current = prompt_history[0] if prompt_history else "0"
 
 
192
  if current != last_prompt and current != "0":
193
  print(f"[BRIDGE] New prompt: {current}")
194
  decoded = decode_text(current)
195
  print(f"[BRIDGE] Decoded: {decoded}")
196
- ai_response = call_ai(decoded)
 
 
 
197
  print(f"[BRIDGE] AI response: {ai_response[:100]}...")
 
198
  encoded_response = encode_text(ai_response)
199
- cloud.set_cloud_variable(variable_name="response", value=encoded_response)
200
  print("[BRIDGE] Response sent to Scratch")
 
201
  last_prompt = current
202
  time.sleep(1)
203
  except Exception as e:
@@ -212,7 +140,7 @@ app = Flask(__name__)
212
 
213
  @app.route('/')
214
  def health():
215
- return "Scratch AI Bridge is running with proxy rotation."
216
 
217
  if __name__ == '__main__':
218
  print("[MAIN] Starting bridge thread...")
 
3
  import os
4
  import random
5
  import requests
6
+ import ssl
7
  from flask import Flask
8
+ import scratchattach as sa
9
+ import websocket
10
 
11
  # ==================== CONFIGURATION ====================
 
12
  SCRATCH_USER = os.environ.get("SCRATCH_USER")
13
  SCRATCH_PASS = os.environ.get("SCRATCH_PASS")
14
  PROJECT_ID = int(os.environ.get("PROJECT_ID", 0))
15
  AI_URL = "https://16dvnk-spirit-kings-ai.hf.space/generate"
16
 
17
+ # Scratch cloud WebSocket IP addresses (from your dig output)
18
+ SCRATCH_CLOUD_IPS = [
19
+ "16.144.201.211",
20
+ "54.69.147.69",
21
+ "44.230.13.252"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  ]
23
 
24
  # ==================== ENCODING/DECODING ====================
 
51
  i += 1
52
  return "".join(result)
53
 
54
+ # ==================== FORCE IP FOR WEBSOCKET ====================
55
+ def patch_scratchattach_websocket():
56
+ """Monkey-patch scratchattach to use IP address with custom Host header."""
57
+ # Pick a random working IP from the list
58
+ working_ip = random.choice(SCRATCH_CLOUD_IPS)
59
+ new_cloud_host = f"wss://{working_ip}/"
60
+
61
+ # Save original CloudConnection class and its connect method
62
+ from scratchattach import CloudConnection
63
+ original_connect = CloudConnection.connect
64
+
65
+ def patched_connect(self, *args, **kwargs):
66
+ # Override the cloud host for this instance
67
+ self.CLOUD_HOST = new_cloud_host
68
+ # Create WebSocket with custom Host header and disabled SSL verification
69
+ self.ws = websocket.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE})
70
+ self.ws.connect(
71
+ self.CLOUD_HOST,
72
+ header=[f"Host: clouddata.scratch.mit.edu"],
73
+ http_proxy_host=None,
74
+ http_proxy_port=None
75
+ )
76
+ self.ws.sock.setblocking(0)
77
+ # Send handshake
78
+ handshake = {"method": "handshake", "project_id": str(self.project_id)}
79
+ if self.user:
80
+ handshake["user"] = self.user
81
+ if self.project_token:
82
+ handshake["project_token"] = self.project_token
83
+ self.ws.send(sa.json.dumps(handshake))
84
+ response = self.ws.recv()
85
+ data = sa.json.loads(response)
86
+ self.session_id = data["session_id"]
87
+ self.project_id = data["project_id"]
88
+ self.user = data.get("user")
89
+ self.permissions = data.get("permissions", {})
90
+ self.ws.settimeout(None)
91
+
92
+ # Apply the patch
93
+ CloudConnection.connect = patched_connect
94
+ print(f"[PATCH] CloudConnection patched to use {new_cloud_host}")
 
 
 
 
 
 
95
 
96
  # ==================== BRIDGE WORKER ====================
97
  def bridge_worker():
98
+ print("[BRIDGE] Starting bridge...")
99
+ patch_scratchattach_websocket()
100
+
101
  while True:
102
  try:
103
  print("[BRIDGE] Logging into Scratch...")
104
+ session = sa.login(SCRATCH_USER, SCRATCH_PASS)
105
+ print("[BRIDGE] Login successful. Connecting to cloud variables...")
106
+ conn = session.connect_cloud(PROJECT_ID)
107
+ print(f"[BRIDGE] Connected to project {PROJECT_ID}")
 
 
108
 
109
  last_prompt = ""
110
  while True:
111
+ # Get current prompt value
112
+ current = conn.get_var("prompt")
113
+ if current is None:
114
+ current = "0"
115
  if current != last_prompt and current != "0":
116
  print(f"[BRIDGE] New prompt: {current}")
117
  decoded = decode_text(current)
118
  print(f"[BRIDGE] Decoded: {decoded}")
119
+
120
+ # AI response (direct call, no proxy)
121
+ response = requests.post(AI_URL, json={"prompt": decoded}, timeout=30)
122
+ ai_response = response.json().get("response", "Sorry, no response.") if response.status_code == 200 else f"Error {response.status_code}"
123
  print(f"[BRIDGE] AI response: {ai_response[:100]}...")
124
+
125
  encoded_response = encode_text(ai_response)
126
+ conn.set_var("response", encoded_response)
127
  print("[BRIDGE] Response sent to Scratch")
128
+
129
  last_prompt = current
130
  time.sleep(1)
131
  except Exception as e:
 
140
 
141
  @app.route('/')
142
  def health():
143
+ return "Scratch AI Bridge is running."
144
 
145
  if __name__ == '__main__':
146
  print("[MAIN] Starting bridge thread...")