Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
aae6a1a
Add Classroom alerts parser and update Classroom private activity par…
faretek1 Apr 13, 2025
62d8e33
comment for clarification for pr
faretek1 Apr 13, 2025
1324d8b
docs: docstrings
faretek1 May 5, 2025
d3fb5e5
Update bug_report.yaml
TheCommCraft May 10, 2025
1afffb6
Update bug_report.yaml
TheCommCraft May 10, 2025
52bc22b
Update bug_report.yaml
TheCommCraft May 10, 2025
41ecf9a
Merge pull request #396 from FAReTek1/educatoralert
faretek1 May 18, 2025
b4cccdc
Fixed a bug (when using login by username / pw, the session id was sa…
TimMcCool May 23, 2025
5ef9e93
Aktualisieren von user.py
TheCommCraft May 28, 2025
aa143fa
fixed #402
TheCommCraft May 29, 2025
35a0463
fix: use typing_extesnsions
faretek1 May 29, 2025
0c88786
fix: use typing_extesnsions
faretek1 May 29, 2025
ee55ed3
Aktualisieren von session.py
TheCommCraft May 30, 2025
c38fc6d
Aktualisieren von session.py
TheCommCraft May 30, 2025
efdca88
Classroom property (solves #382) (#405)
faretek1 May 30, 2025
18232fd
merge (#406)
faretek1 May 30, 2025
596538d
improvements
TheCommCraft May 30, 2025
40ee812
revert #406 (#407)
faretek1 May 30, 2025
07a4cce
Merge branch 'main' of https://github.com/TimMcCool/scratchattach
TheCommCraft May 30, 2025
5ad1f5a
Merge branch 'main' of https://github.com/TimMcCool/scratchattach
TheCommCraft May 30, 2025
3396932
Aktualisieren von session.py
TheCommCraft Jun 1, 2025
16eb54b
partially fixed type safety
TheCommCraft Jun 1, 2025
966dd2b
Merge branch 'main' of https://github.com/TimMcCool/scratchattach
TheCommCraft Jun 1, 2025
add5546
a few fixes
TheCommCraft Jun 1, 2025
473635d
improvements
TheCommCraft Jun 1, 2025
efa5b5b
sb2 json fetch (#417)
faretek1 Jun 2, 2025
f388112
feat: decode_session_id - to solve #395 (#409)
faretek1 Jun 3, 2025
2324df4
edit: classroom alert attr order (#418)
faretek1 Jun 3, 2025
dc4f79d
edit: assert xtoken equality (#419)
faretek1 Jun 3, 2025
0ac323c
docstrings: sa.editor & minor code changes (#427)
faretek1 Jun 3, 2025
4bc14ec
dataclass for forumpost
TheCommCraft Jun 4, 2025
7054d80
Merge branch 'main' of https://github.com/TimMcCool/scratchattach
TheCommCraft Jun 4, 2025
40aed13
requirements + no match statement
TheCommCraft Jun 4, 2025
0591a25
replace parent imports with first party imports (#428)
TheCommCraft Jun 4, 2025
bbe294f
edit: classroom dataclass (#431)
faretek1 Jun 7, 2025
e932ede
edit: session as dataclass (#434)
faretek1 Jun 7, 2025
86b45bd
Scratch text representation (#435)
TheCommCraft Jun 9, 2025
4b829b3
fix
TheCommCraft Jun 9, 2025
53d4fc6
comment positions
TheCommCraft Jun 9, 2025
71f6b86
improved
TheCommCraft Jun 9, 2025
0b45d2f
Type safety improvements
TheCommCraft Jun 12, 2025
df45b7b
singleton type safety
TheCommCraft Jun 12, 2025
78b7b68
base.py type safety
TheCommCraft Jun 12, 2025
54d975e
commons.SpriteInput + more type safety
TheCommCraft Jun 12, 2025
ee9e170
project_urls
TheCommCraft Jun 17, 2025
b7c05eb
LockEvent fix
TheCommCraft Jun 22, 2025
a104097
better LockEvent implementation
TheCommCraft Jun 22, 2025
e395579
Add files via upload
TimMcCool Jul 27, 2025
9d4cf79
Update 1 user.py (still wip on the file)
Boss-1s Jul 28, 2025
c6edffa
Update user.py
Boss-1s Jul 29, 2025
0f0ba70
Update user.py
Boss-1s Jul 29, 2025
31b64af
Merge branch 'TimMcCool:main' into main
Boss-1s Jul 30, 2025
4f36bd8
Update user.py with the changes suggested by @TheCommCraft
Boss-1s Jul 31, 2025
4b17b4f
Update user.py
Boss-1s Jul 31, 2025
a3dd020
Update user.py
TheCommCraft Jul 31, 2025
d10921c
Update user.py
TheCommCraft Jul 31, 2025
f43bb6e
Update user.py
TheCommCraft Jul 31, 2025
b168ecc
Update user.py
TheCommCraft Jul 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,12 @@ body:
label: What happened?
validations:
required: true
- type: dropdown
id: cloudvariables-location
- type: textarea
id: code
attributes:
label: Cloud variables
description: If your bug is based in some form on cloud variables, which cloud variable server did you use? If your bug doesn't have anything to do with cloud variables, submit "No cloud vars"
options:
- Scratch
- TurboWarp
- Custom Cloud Server
- Other
- No cloud vars
default: 0
label: Your code.
description: Put your code here. Be careful not to reveal your login data.
render: python
validations:
required: true
- type: textarea
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ scratchattach.code-workspace
**/.DS_Store
setup.py
setup.py
.env
.env
tests/manual_tests/**
Binary file modified assets/CloudRequests_Template.sb3
Binary file not shown.
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ websocket-client
requests
bs4
SimpleWebSocketServer
typing-extensions
browser_cookie3
14 changes: 7 additions & 7 deletions scratchattach/cloud/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ def close(self) -> None:

import websocket

from ..site import session
from ..eventhandlers import cloud_recorder
from ..utils import exceptions
from ..eventhandlers.cloud_requests import CloudRequests
from ..eventhandlers.cloud_events import CloudEvents
from ..eventhandlers.cloud_storage import CloudStorage
from ..site import cloud_activity
from scratchattach.site import session
from scratchattach.eventhandlers import cloud_recorder
from scratchattach.utils import exceptions
from scratchattach.eventhandlers.cloud_requests import CloudRequests
from scratchattach.eventhandlers.cloud_events import CloudEvents
from scratchattach.eventhandlers.cloud_storage import CloudStorage
from scratchattach.site import cloud_activity

T = TypeVar("T")

Expand Down
8 changes: 4 additions & 4 deletions scratchattach/cloud/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

from ._base import BaseCloud
from typing import Type
from ..utils.requests import Requests as requests
from ..utils import exceptions, commons
from ..site import cloud_activity
from scratchattach.utils.requests import requests
from scratchattach.utils import exceptions, commons
from scratchattach.site import cloud_activity


class ScratchCloud(BaseCloud):
Expand Down Expand Up @@ -88,7 +88,7 @@ def get_all_vars(self, *, use_logs=False):

def events(self, *, use_logs=False):
if self._session is None or use_logs:
from ..eventhandlers.cloud_events import CloudLogEvents
from scratchattach.eventhandlers.cloud_events import CloudLogEvents
return CloudLogEvents(self)
else:
return super().events()
Expand Down
64 changes: 59 additions & 5 deletions scratchattach/editor/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@

@dataclass(init=True, repr=True)
class AssetFile:
"""
Represents the file information for an asset
- stores the filename, data, and md5 hash
"""
filename: str
_data: bytes = field(repr=False, default=None)
_md5: str = field(repr=False, default=None)
_data: bytes = field(repr=False, default_factory=bytes)
_md5: str = field(repr=False, default_factory=str)

@property
def data(self):
"""
Return the contents of the asset file, as bytes
"""
if self._data is None:
# Download and cache
rq = requests.get(f"https://assets.scratch.mit.edu/internalapi/asset/{self.filename}/get/")
Expand All @@ -28,6 +35,9 @@ def data(self):

@property
def md5(self):
"""
Compute/retrieve the md5 hash value of the asset file data
"""
if self._md5 is None:
self._md5 = md5(self.data).hexdigest()

Expand All @@ -38,7 +48,7 @@ class Asset(base.SpriteSubComponent):
def __init__(self,
name: str = "costume1",
file_name: str = "b7853f557e4426412e64bb3da6531a99.svg",
_sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
_sprite: commons.SpriteInput = build_defaulting.SPRITE_DEFAULT):
"""
Represents a generic asset. Can be a sound or an image.
https://en.scratch-wiki.info/wiki/Scratch_File_Format#Assets
Expand All @@ -60,29 +70,50 @@ def __repr__(self):

@property
def folder(self):
"""
Get the folder name of this asset, based on the asset name. Uses the turbowarp syntax
"""
return commons.get_folder_name(self.name)

@property
def name_nfldr(self):
"""
Get the asset name after removing the folder name
"""
return commons.get_name_nofldr(self.name)

@property
def file_name(self):
"""
Get the exact file name, as it would be within an sb3 file
equivalent to the md5ext value using in scratch project JSON
"""
return f"{self.id}.{self.data_format}"

@property
def md5ext(self):
"""
Get the exact file name, as it would be within an sb3 file
equivalent to the md5ext value using in scratch project JSON
"""
return self.file_name

@property
def parent(self):
"""
Return the project that this asset is attached to. If there is no attached project,
try returning the attached sprite
"""
if self.project is None:
return self.sprite
else:
return self.project

@property
def asset_file(self) -> AssetFile:
"""
Get the associated asset file object for this asset object
"""
for asset_file in self.parent.asset_data:
if asset_file.filename == self.file_name:
return asset_file
Expand All @@ -94,17 +125,27 @@ def asset_file(self) -> AssetFile:

@staticmethod
def from_json(data: dict):
"""
Load asset data from project.json
"""
_name = data.get("name")
assert isinstance(_name, str)
_file_name = data.get("md5ext")
if _file_name is None:
if "dataFormat" in data and "assetId" in data:
_id = data["assetId"]
_data_format = data["dataFormat"]
_file_name = f"{_id}.{_data_format}"
else:
_file_name = ""
assert isinstance(_file_name, str)

return Asset(_name, _file_name)

def to_json(self) -> dict:
"""
Convert asset data to project.json format
"""
return {
"name": self.name,

Expand All @@ -113,6 +154,7 @@ def to_json(self) -> dict:
"dataFormat": self.data_format,
}

# todo: implement below:
"""
@staticmethod
def from_file(fp: str, name: str = None):
Expand All @@ -132,9 +174,9 @@ def __init__(self,
bitmap_resolution=None,
rotation_center_x: int | float = 48,
rotation_center_y: int | float = 50,
_sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
_sprite: commons.SpriteInput = build_defaulting.SPRITE_DEFAULT):
"""
A costume. An asset with additional properties
A costume (image). An asset with additional properties
https://en.scratch-wiki.info/wiki/Scratch_File_Format#Costumes
"""
super().__init__(name, file_name, _sprite)
Expand All @@ -145,6 +187,9 @@ def __init__(self,

@staticmethod
def from_json(data):
"""
Load costume data from project.json
"""
_asset_load = Asset.from_json(data)

bitmap_resolution = data.get("bitmapResolution")
Expand All @@ -156,6 +201,9 @@ def from_json(data):
bitmap_resolution, rotation_center_x, rotation_center_y)

def to_json(self) -> dict:
"""
Convert costume to project.json format
"""
_json = super().to_json()
_json.update({
"bitmapResolution": self.bitmap_resolution,
Expand Down Expand Up @@ -184,13 +232,19 @@ def __init__(self,

@staticmethod
def from_json(data):
"""
Load sound from project.json
"""
_asset_load = Asset.from_json(data)

rate = data.get("rate")
sample_count = data.get("sampleCount")
return Sound(_asset_load.name, _asset_load.file_name, rate, sample_count)

def to_json(self) -> dict:
"""
Convert Sound to project.json format
"""
_json = super().to_json()
commons.noneless_update(_json, {
"rate": self.rate,
Expand Down
Loading