From 252d27690d2d7fe1504ae68674f4097eb67bdcff Mon Sep 17 00:00:00 2001 From: Boss_1s <95505913+Boss-1s@users.noreply.github.com> Date: Thu, 7 May 2026 17:23:01 -0500 Subject: [PATCH 1/6] refactor: Add try-except blocks for loading json on connect and reading data #537 Signed-off-by: Boss_1s <95505913+Boss-1s@users.noreply.github.com> --- scratchattach/eventhandlers/cloud_server.py | 32 ++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/scratchattach/eventhandlers/cloud_server.py b/scratchattach/eventhandlers/cloud_server.py index d950f56f..10c78502 100644 --- a/scratchattach/eventhandlers/cloud_server.py +++ b/scratchattach/eventhandlers/cloud_server.py @@ -131,18 +131,30 @@ def handleMessage(self): if self.server.check_for_ip_ban(self): return - data = json.loads(self.data) + try: + data = json.loads(self.data) + except json.decoder.JSONDecodeError: + print(f"Warning! Client {self.address[0] + ":" + str(self.address[1])} sent invalid JSON to the server. ", + "The client may be unsafe, please stay alert." + ) print(data) - if data["method"] == "set": - self.handle_set(data) - elif data["method"] == "handshake": - self.handle_handshake(data) - else: + try: + if data["method"] == "set": + self.handle_set(data) + elif data["method"] == "handshake": + self.handle_handshake(data) + else: + print( + "Error:", + self.address[0] + ":" + str(self.address[1]), + "sent a message without providing a valid method (set, handshake)", + ) + except KeyError: print( - "Error:", - self.address[0] + ":" + str(self.address[1]), - "sent a message without providing a valid method (set, handshake)", + "Error:", + self.address[0] + ":" + str(self.address[1]), + "sent a message without providing a valid method (set, handshake)", ) except Exception as e: @@ -344,7 +356,7 @@ def resume(self): self.running = True def stop(self, wait_call_threads: bool = True): - BaseEventHandler.stop(self, wait_call_threads) + BaseEventHandler.stop(self, wait_call_threads) # wait_call_threads does not exist in BaseEventHandler.stop self.close() From 7fdfe0fcef8e0ca85cf78b24a3f1d90945b7af33 Mon Sep 17 00:00:00 2001 From: Boss_1s <95505913+Boss-1s@users.noreply.github.com> Date: Thu, 7 May 2026 18:10:37 -0500 Subject: [PATCH 2/6] Finish debug for json loading and reading 'method' key Signed-off-by: Boss_1s <95505913+Boss-1s@users.noreply.github.com> --- scratchattach/eventhandlers/cloud_server.py | 24 +++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/scratchattach/eventhandlers/cloud_server.py b/scratchattach/eventhandlers/cloud_server.py index 10c78502..621b23e9 100644 --- a/scratchattach/eventhandlers/cloud_server.py +++ b/scratchattach/eventhandlers/cloud_server.py @@ -137,9 +137,17 @@ def handleMessage(self): print(f"Warning! Client {self.address[0] + ":" + str(self.address[1])} sent invalid JSON to the server. ", "The client may be unsafe, please stay alert." ) - print(data) + return - try: + print(f"Data recieved: {data}") + if data == {}: + print( + "Error:", + self.address[0] + ":" + str(self.address[1]), + "sent a blank JSON message. If this seems suspicious, ban the IP.", + ) + return + if 'method' in data: if data["method"] == "set": self.handle_set(data) elif data["method"] == "handshake": @@ -149,16 +157,18 @@ def handleMessage(self): "Error:", self.address[0] + ":" + str(self.address[1]), "sent a message without providing a valid method (set, handshake)", + f"but provided method {list(data.values())[0]} instead.", ) - except KeyError: + else: print( - "Error:", - self.address[0] + ":" + str(self.address[1]), - "sent a message without providing a valid method (set, handshake)", + "Error:", + self.address[0] + ":" + str(self.address[1]), + "sent a message without providing a valid 'method' key,", + f"but provided key {list(data.keys())[0]} instead.", ) except Exception as e: - print("Internal error in handleMessage:", e, traceback.format_exc()) + print("Internal error in handleMessage:", e, "\n", traceback.format_exc()) def handleConnected(self): if not self.server.running: From a44c3f5be3d3da946d696c960123009b0b1a2891 Mon Sep 17 00:00:00 2001 From: Boss_1s <95505913+Boss-1s@users.noreply.github.com> Date: Fri, 8 May 2026 15:52:51 -0500 Subject: [PATCH 3/6] a little more context on the ip banned message Signed-off-by: Boss_1s <95505913+Boss-1s@users.noreply.github.com> --- scratchattach/eventhandlers/cloud_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratchattach/eventhandlers/cloud_server.py b/scratchattach/eventhandlers/cloud_server.py index 621b23e9..33834d71 100644 --- a/scratchattach/eventhandlers/cloud_server.py +++ b/scratchattach/eventhandlers/cloud_server.py @@ -250,7 +250,7 @@ def check_for_ip_ban(self, client): ): client.sendMessage("You have been banned from this server") client.close(4002) - print(client.address[0] + ":" + str(client.address[1]), "(IP-banned) was disconnected") + print(client.address[0] + ":" + str(client.address[1]), "(IP-banned) was forced disconnected") return True return False From a05828e4661eb9301048c8f2110712644117ae11 Mon Sep 17 00:00:00 2001 From: Boss_1s <95505913+Boss-1s@users.noreply.github.com> Date: Fri, 8 May 2026 21:14:30 -0500 Subject: [PATCH 4/6] [UNIMPORTANT] notated unsued import Signed-off-by: Boss_1s <95505913+Boss-1s@users.noreply.github.com> --- scratchattach/eventhandlers/cloud_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scratchattach/eventhandlers/cloud_server.py b/scratchattach/eventhandlers/cloud_server.py index 33834d71..12bc7cbe 100644 --- a/scratchattach/eventhandlers/cloud_server.py +++ b/scratchattach/eventhandlers/cloud_server.py @@ -1,7 +1,7 @@ from __future__ import annotations from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket -from threading import Thread +from threading import Thread # unused threading.Thread? not changing, just noting -Boss_1s from scratchattach.utils import exceptions import json import time From 435d5f524dfa7023481b3db9fe94d41dbafb034b Mon Sep 17 00:00:00 2001 From: Boss_1s <95505913+Boss-1s@users.noreply.github.com> Date: Tue, 25 Aug 2026 19:19:16 +0000 Subject: [PATCH 5/6] feat: rich Signed-off-by: GitHub --- scratchattach/eventhandlers/cloud_server.py | 146 +++++++++++--------- 1 file changed, 82 insertions(+), 64 deletions(-) diff --git a/scratchattach/eventhandlers/cloud_server.py b/scratchattach/eventhandlers/cloud_server.py index 12bc7cbe..2bdf8cfa 100644 --- a/scratchattach/eventhandlers/cloud_server.py +++ b/scratchattach/eventhandlers/cloud_server.py @@ -1,14 +1,15 @@ from __future__ import annotations -from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket -from threading import Thread # unused threading.Thread? not changing, just noting -Boss_1s -from scratchattach.utils import exceptions import json import time +import traceback +from threading import Thread # NOTE: unused threading.Thread? not changing, just noting -Boss_1s +from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket +from rich import print +from scratchattach.utils import exceptions from scratchattach.site import cloud_activity from scratchattach.site.user import User from ._base import BaseEventHandler -import traceback class TwCloudSocket(WebSocket): @@ -21,29 +22,36 @@ def handle_set(self, data: dict): if data["project_id"] not in self.server.whitelisted_projects: self.close(4002) if self.server.log_var_sets: - print( - self.address[0] + ":" + str(self.address[1]), - "tried to set a var on non-whitelisted project and was disconnected, project:", - data["project_id"], - "user:", - data["user"], + print("[red]Error: "+ + self.address[0] + ":" + str(self.address[1])+ + " with username "+ + data["user"]+ + " tried to set a var on non-whitelisted project ID "+ + data["project_id"]+ + " and was disconnected.[/]" ) return # check if value is valid if not self.server._check_value(data["value"]): if self.server.log_var_sets: - print(self.address[0] + ":" + str(self.address[1]), "sent an invalid var value") + print("[yellow]Warning: "+ + self.address[0] + ":" + str(self.address[1])+ + " sent an invalid variable value.[/]\n"+ + f" Value: {data["value"]}") return # perform cloud var and forward to other players if self.server.log_var_sets: print( - self.address[0] + ":" + str(self.address[1]), - f"set {data['name']} to {data['value']}, project:", - str(data["project_id"]), - "user:", - data["user"], + self.address[0] + ":" + str(self.address[1])+ + f" with username {data['user']}"+ + f" sucessfully set {data['name']} to {data['value']} in project "+ + f"{str(data['project_id'])}." ) - self.server.set_var(data["project_id"], data["name"], data["value"], user=data["user"], skip_forward=self) + self.server.set_var(data["project_id"], + data["name"], + data["value"], + user=data["user"], + skip_forward=self) send_to_clients = { "method": "set", "user": data["user"], @@ -61,45 +69,53 @@ def handle_set(self, data: dict): def handle_handshake(self, data: dict): # check if handshake is valid - if not "user" in data: - print(self.address[0] + ":" + str(self.address[1]), "tried to handshake without providing a username") + if not data["user"]: + print("[red]Error: "+ + str(self.address[0]) + ":" + str(self.address[1])+ + " tried to handshake without providing a username.[/]") self.close(4002) return - if not "project_id" in data: - print(self.address[0] + ":" + str(self.address[1]), "tried to handshake without providing a project_id") + if not data["project_id"]: + print("[red]Error: "+ + str(self.address[0]) + ":" + str(self.address[1])+ + " tried to handshake without providing a project_id.[/]") self.close(4002) return + # check if project_id is in username is allowed - if self.server.allow_nonscratch_names is False: + if not self.server.allow_nonscratch_names: if not User(username=data["user"]).does_exist(): - print( - self.address[0] + ":" + str(self.address[1]), - "tried to handshake using a username not existing on Scratch, project:", - data["project_id"], - "user:", - data["user"], + print("[red]Error: "+ + str(self.address[0]) + ":" + str(self.address[1])+ + " tried to handshake with non-existent Scratch username "+ + data["user"]+ + ".[/]" ) self.close(4002) return + # check if project_id is in whitelisted projects (if there's a list of whitelisted projects) if self.server.whitelisted_projects is not None: if str(data["project_id"]) not in self.server.whitelisted_projects: self.close(4002) - print( - self.address[0] + ":" + str(self.address[1]), - "tried to handshake on a non-whitelisted project:", - data["project_id"], - "user:", - data["user"], + print("[red]Error: "+ + str(self.address[0]) + ":" + str(self.address[1])+ + " with username "+ + data["user"]+ + " tried to handshake on a non-whitelisted project with ID "+ + data["project_id"]+ + ".[/]" ) return # register handshake in users list (save username and project_id) - print( - self.address[0] + ":" + str(self.address[1]), - "handshaked, project:", - data["project_id"], - "user:", - data["user"], + print("[green b]Handshake successful![/]\n"+ + "[green] Address "+ + str(self.address[0]) + ":" + str(self.address[1])+ + " under username [b]"+ + data["user"]+ + "[/] and project ID [b]"+ + data["project_id"]+ + " sucessfully handshaked with the server.[/green]" ) self.server.tw_clients[self.address]["username"] = data["user"] self.server.tw_clients[self.address]["project_id"] = data["project_id"] @@ -134,17 +150,16 @@ def handleMessage(self): try: data = json.loads(self.data) except json.decoder.JSONDecodeError: - print(f"Warning! Client {self.address[0] + ":" + str(self.address[1])} sent invalid JSON to the server. ", - "The client may be unsafe, please stay alert." - ) + print(f"[yellow]Warning: Client {str(self.address[0]) + ':' + str(self.address[1])} sent"+ + " invalid JSON to the server. The client may be unsafe, please stay alert.[/]\n"+ + f" [b]Data received:[/] {self.data}") return - print(f"Data recieved: {data}") if data == {}: print( - "Error:", - self.address[0] + ":" + str(self.address[1]), - "sent a blank JSON message. If this seems suspicious, ban the IP.", + "[yellow]Warning: "+ + str(self.address[0]) + ":" + str(self.address[1])+ + " sent a blank JSON message. [b]If this seems suspicious, ban the IP.[/][/]", ) return if 'method' in data: @@ -154,21 +169,22 @@ def handleMessage(self): self.handle_handshake(data) else: print( - "Error:", - self.address[0] + ":" + str(self.address[1]), - "sent a message without providing a valid method (set, handshake)", - f"but provided method {list(data.values())[0]} instead.", + "[yellow]Warning: "+ + str(self.address[0]) + ":" + str(self.address[1]), + " sent a message without providing a valid method (either [b]set[/b] or [b]handshake[/b]),"+ + f"but provided method '{list(data.values())[0]}' instead.[/]\n", + f" [b]Data received:[/] {self.data}" ) else: print( - "Error:", - self.address[0] + ":" + str(self.address[1]), - "sent a message without providing a valid 'method' key,", - f"but provided key {list(data.keys())[0]} instead.", + "[yellow]Warning: "+ + str(self.address[0]) + ":" + str(self.address[1])+ + " sent a message without providing a valid [b]'method'[/b] key,"+ + f" but provided key '{list(data.keys())[0]}' instead.[/]\n", + f" [b]Data received:[/] {self.data}" ) - except Exception as e: - print("Internal error in handleMessage:", e, "\n", traceback.format_exc()) + print(f"[red]Internal error in handleMessage: {e}[/]\n", traceback.format_exc()) def handleConnected(self): if not self.server.running: @@ -177,19 +193,20 @@ def handleConnected(self): if self.server.check_for_ip_ban(self): return - print(self.address[0] + ":" + str(self.address[1]), "connected") + print("[green]New client " + str(self.address[0]) + ":" + str(self.address[1]) + " connected![/]") self.server.tw_clients[self.address] = {"client": self, "username": None, "project_id": None} - # raise event + # raise connect event self.server.call_event("on_connect", [self]) except Exception as e: - print("Internal error in handleConntected:", e) + print(f"[red]Internal error in handleConnected: {e} [/]\n", traceback.format_exc()) def handleClose(self): if not self.server.running: return + try: if self.address in self.server.tw_clients: - # raise event + # raise disconnect event self.server.call_event( "on_disconnect", [ @@ -198,9 +215,9 @@ def handleClose(self): self, ], ) - print(self.address[0] + ":" + str(self.address[1]), "disconnected") + print(f"[blue]Client {self.address[0]}:{self.address[1]} disconnected from server sucessfully.[/]") except Exception as e: - print("Internal error in handleClose:", e) + print(f"[red]Internal error in handleClose: {e} [/]\n", traceback.format_exc()) class TwCloudServer(SimpleWebSocketServer, BaseEventHandler): @@ -250,7 +267,8 @@ def check_for_ip_ban(self, client): ): client.sendMessage("You have been banned from this server") client.close(4002) - print(client.address[0] + ":" + str(client.address[1]), "(IP-banned) was forced disconnected") + print(f"[yellow]Client {client.address[0]}:{client.address[1]} was forced disconnected "+ + "due to IP ban. [b]If this dosen't look right, remove them from the list.[/][/]") return True return False From db50b0bf55194beda4cc39b4c88548030ecc3daa Mon Sep 17 00:00:00 2001 From: Boss_1s <95505913+Boss-1s@users.noreply.github.com> Date: Tue, 25 Aug 2026 19:19:32 +0000 Subject: [PATCH 6/6] bump ruff up a patch Signed-off-by: GitHub --- uv.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index 813c5014..db8d358f 100644 --- a/uv.lock +++ b/uv.lock @@ -833,7 +833,7 @@ requires-dist = [ provides-extras = ["cli", "lark"] [package.metadata.requires-dev] -dev = [{ name = "ruff", specifier = ">=0.16.0" }] +dev = [{ name = "ruff", specifier = ">=0.16.1" }] [[package]] name = "shadowcopy"