scratchattach 2.1.14__py3-none-any.whl → 3.0.0b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scratchattach/__init__.py +14 -6
- scratchattach/__main__.py +93 -0
- {scratchattach-2.1.14.dist-info → scratchattach-3.0.0b0.dist-info}/METADATA +7 -11
- scratchattach-3.0.0b0.dist-info/RECORD +8 -0
- {scratchattach-2.1.14.dist-info → scratchattach-3.0.0b0.dist-info}/WHEEL +1 -1
- scratchattach-3.0.0b0.dist-info/entry_points.txt +2 -0
- scratchattach/cloud/__init__.py +0 -2
- scratchattach/cloud/_base.py +0 -458
- scratchattach/cloud/cloud.py +0 -183
- scratchattach/editor/__init__.py +0 -21
- scratchattach/editor/asset.py +0 -253
- scratchattach/editor/backpack_json.py +0 -117
- scratchattach/editor/base.py +0 -193
- scratchattach/editor/block.py +0 -579
- scratchattach/editor/blockshape.py +0 -357
- scratchattach/editor/build_defaulting.py +0 -51
- scratchattach/editor/code_translation/__init__.py +0 -0
- scratchattach/editor/code_translation/parse.py +0 -177
- scratchattach/editor/comment.py +0 -80
- scratchattach/editor/commons.py +0 -306
- scratchattach/editor/extension.py +0 -50
- scratchattach/editor/field.py +0 -99
- scratchattach/editor/inputs.py +0 -135
- scratchattach/editor/meta.py +0 -114
- scratchattach/editor/monitor.py +0 -183
- scratchattach/editor/mutation.py +0 -324
- scratchattach/editor/pallete.py +0 -90
- scratchattach/editor/prim.py +0 -170
- scratchattach/editor/project.py +0 -279
- scratchattach/editor/sprite.py +0 -599
- scratchattach/editor/twconfig.py +0 -114
- scratchattach/editor/vlb.py +0 -134
- scratchattach/eventhandlers/__init__.py +0 -0
- scratchattach/eventhandlers/_base.py +0 -100
- scratchattach/eventhandlers/cloud_events.py +0 -110
- scratchattach/eventhandlers/cloud_recorder.py +0 -26
- scratchattach/eventhandlers/cloud_requests.py +0 -459
- scratchattach/eventhandlers/cloud_server.py +0 -246
- scratchattach/eventhandlers/cloud_storage.py +0 -136
- scratchattach/eventhandlers/combine.py +0 -30
- scratchattach/eventhandlers/filterbot.py +0 -161
- scratchattach/eventhandlers/message_events.py +0 -42
- scratchattach/other/__init__.py +0 -0
- scratchattach/other/other_apis.py +0 -284
- scratchattach/other/project_json_capabilities.py +0 -475
- scratchattach/site/__init__.py +0 -0
- scratchattach/site/_base.py +0 -66
- scratchattach/site/activity.py +0 -382
- scratchattach/site/alert.py +0 -227
- scratchattach/site/backpack_asset.py +0 -118
- scratchattach/site/browser_cookie3_stub.py +0 -17
- scratchattach/site/browser_cookies.py +0 -61
- scratchattach/site/classroom.py +0 -447
- scratchattach/site/cloud_activity.py +0 -107
- scratchattach/site/comment.py +0 -242
- scratchattach/site/forum.py +0 -432
- scratchattach/site/project.py +0 -825
- scratchattach/site/session.py +0 -1238
- scratchattach/site/studio.py +0 -611
- scratchattach/site/user.py +0 -956
- scratchattach/utils/__init__.py +0 -0
- scratchattach/utils/commons.py +0 -255
- scratchattach/utils/encoder.py +0 -158
- scratchattach/utils/enums.py +0 -236
- scratchattach/utils/exceptions.py +0 -243
- scratchattach/utils/requests.py +0 -93
- scratchattach-2.1.14.dist-info/RECORD +0 -66
- {scratchattach-2.1.14.dist-info → scratchattach-3.0.0b0.dist-info}/licenses/LICENSE +0 -0
- {scratchattach-2.1.14.dist-info → scratchattach-3.0.0b0.dist-info}/top_level.txt +0 -0
scratchattach/editor/vlb.py
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Variables, lists & broadcasts
|
|
3
|
-
"""
|
|
4
|
-
# Perhaps ids should not be stored in these objects, but in the sprite, similarly
|
|
5
|
-
# to how blocks/prims are stored
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from typing import Optional, Literal
|
|
10
|
-
|
|
11
|
-
from . import base, sprite, build_defaulting
|
|
12
|
-
from scratchattach.utils import exceptions
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Variable(base.NamedIDComponent):
|
|
16
|
-
def __init__(self, _id: str, _name: str, _value: Optional[str | int | float] = None, _is_cloud: bool = False,
|
|
17
|
-
_sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
|
|
18
|
-
"""
|
|
19
|
-
Class representing a variable.
|
|
20
|
-
https://en.scratch-wiki.info/wiki/Scratch_File_Format#Targets:~:text=variables,otherwise%20not%20present
|
|
21
|
-
"""
|
|
22
|
-
if _value is None:
|
|
23
|
-
_value = 0
|
|
24
|
-
|
|
25
|
-
self.value = _value
|
|
26
|
-
self.is_cloud = _is_cloud
|
|
27
|
-
|
|
28
|
-
super().__init__(_id, _name, _sprite)
|
|
29
|
-
|
|
30
|
-
@property
|
|
31
|
-
def is_global(self):
|
|
32
|
-
"""
|
|
33
|
-
Works out whethere a variable is global based on whether the sprite is a stage
|
|
34
|
-
:return: Whether this variable is a global variable.
|
|
35
|
-
"""
|
|
36
|
-
return self.sprite.is_stage
|
|
37
|
-
|
|
38
|
-
@staticmethod
|
|
39
|
-
def from_json(data: tuple[str, tuple[str, str | int | float] | tuple[str, str | int | float, bool]]):
|
|
40
|
-
"""
|
|
41
|
-
Read data in format: (variable id, variable JSON)
|
|
42
|
-
"""
|
|
43
|
-
assert len(data) == 2
|
|
44
|
-
_id, data = data
|
|
45
|
-
|
|
46
|
-
assert len(data) in (2, 3)
|
|
47
|
-
_name, _value = data[:2]
|
|
48
|
-
|
|
49
|
-
if len(data) == 3:
|
|
50
|
-
_is_cloud = data[2]
|
|
51
|
-
else:
|
|
52
|
-
_is_cloud = False
|
|
53
|
-
|
|
54
|
-
return Variable(_id, _name, _value, _is_cloud)
|
|
55
|
-
|
|
56
|
-
def to_json(self) -> tuple[str, str | int | float, bool] | tuple[str, str | int | float]:
|
|
57
|
-
"""
|
|
58
|
-
Returns Variable data as a tuple
|
|
59
|
-
"""
|
|
60
|
-
if self.is_cloud:
|
|
61
|
-
_ret = self.name, self.value, True
|
|
62
|
-
else:
|
|
63
|
-
_ret = self.name, self.value
|
|
64
|
-
|
|
65
|
-
return _ret
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class List(base.NamedIDComponent):
|
|
69
|
-
def __init__(self, _id: str, _name: str, _value: Optional[list[str | int | float]] = None,
|
|
70
|
-
_sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
|
|
71
|
-
"""
|
|
72
|
-
Class representing a list.
|
|
73
|
-
https://en.scratch-wiki.info/wiki/Scratch_File_Format#Targets:~:text=lists,as%20an%20array
|
|
74
|
-
"""
|
|
75
|
-
if _value is None:
|
|
76
|
-
_value = []
|
|
77
|
-
|
|
78
|
-
self.value = _value
|
|
79
|
-
super().__init__(_id, _name, _sprite)
|
|
80
|
-
|
|
81
|
-
@staticmethod
|
|
82
|
-
def from_json(data: tuple[str, tuple[str, str | int | float] | tuple[str, str | int | float, bool]]):
|
|
83
|
-
"""
|
|
84
|
-
Read data in format: (variable id, variable JSON)
|
|
85
|
-
"""
|
|
86
|
-
assert len(data) == 2
|
|
87
|
-
_id, data = data
|
|
88
|
-
|
|
89
|
-
assert len(data) == 2
|
|
90
|
-
_name, _value = data
|
|
91
|
-
|
|
92
|
-
return List(_id, _name, _value)
|
|
93
|
-
|
|
94
|
-
def to_json(self) -> tuple[str, tuple[str, str | int | float, bool] | tuple[str, str | int | float]]:
|
|
95
|
-
"""
|
|
96
|
-
Returns List data as a tuple
|
|
97
|
-
"""
|
|
98
|
-
return self.name, self.value
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class Broadcast(base.NamedIDComponent):
|
|
102
|
-
def __init__(self, _id: str, _name: str, _sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
|
|
103
|
-
"""
|
|
104
|
-
Class representing a broadcast.
|
|
105
|
-
https://en.scratch-wiki.info/wiki/Scratch_File_Format#Targets:~:text=broadcasts,in%20the%20stage
|
|
106
|
-
"""
|
|
107
|
-
super().__init__(_id, _name, _sprite)
|
|
108
|
-
|
|
109
|
-
@staticmethod
|
|
110
|
-
def from_json(data: tuple[str, str]):
|
|
111
|
-
assert len(data) == 2
|
|
112
|
-
_id, _name = data
|
|
113
|
-
|
|
114
|
-
return Broadcast(_id, _name)
|
|
115
|
-
|
|
116
|
-
def to_json(self) -> str:
|
|
117
|
-
"""
|
|
118
|
-
:return: Broadcast as JSON (just a string of its name)
|
|
119
|
-
"""
|
|
120
|
-
return self.name
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def construct(vlb_type: Literal["variable", "list", "broadcast"], _id: Optional[str] = None, _name: Optional[str] = None,
|
|
124
|
-
_sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT) -> Variable | List | Broadcast:
|
|
125
|
-
if vlb_type == "variable":
|
|
126
|
-
vlb_type = Variable
|
|
127
|
-
elif vlb_type == "list":
|
|
128
|
-
vlb_type = List
|
|
129
|
-
elif vlb_type == "broadcast":
|
|
130
|
-
vlb_type = Broadcast
|
|
131
|
-
else:
|
|
132
|
-
raise exceptions.InvalidVLBName(f"Bad VLB {vlb_type!r}")
|
|
133
|
-
|
|
134
|
-
return vlb_type(_id, _name, _sprite)
|
|
File without changes
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
from collections import defaultdict
|
|
5
|
-
from threading import Thread
|
|
6
|
-
from collections.abc import Callable
|
|
7
|
-
import traceback
|
|
8
|
-
from scratchattach.utils.requests import requests
|
|
9
|
-
from scratchattach.utils import exceptions
|
|
10
|
-
|
|
11
|
-
class BaseEventHandler(ABC):
|
|
12
|
-
_events: defaultdict[str, list[Callable]]
|
|
13
|
-
_threaded_events: defaultdict[str, list[Callable]]
|
|
14
|
-
|
|
15
|
-
def __init__(self):
|
|
16
|
-
self._thread = None
|
|
17
|
-
self.running = False
|
|
18
|
-
self._events = defaultdict(list)
|
|
19
|
-
self._threaded_events = defaultdict(list)
|
|
20
|
-
|
|
21
|
-
def start(self, *, thread=True, ignore_exceptions=True):
|
|
22
|
-
"""
|
|
23
|
-
Starts the event handler.
|
|
24
|
-
|
|
25
|
-
Keyword Arguments:
|
|
26
|
-
thread (bool): Whether the event handler should be run in a thread.
|
|
27
|
-
ignore_exceptions (bool): Whether to catch exceptions that happen in individual events
|
|
28
|
-
"""
|
|
29
|
-
if self.running is False:
|
|
30
|
-
self.ignore_exceptions = ignore_exceptions
|
|
31
|
-
self.running = True
|
|
32
|
-
if thread:
|
|
33
|
-
self._thread = Thread(target=self._updater, args=())
|
|
34
|
-
self._thread.start()
|
|
35
|
-
else:
|
|
36
|
-
self._thread = None
|
|
37
|
-
self._updater()
|
|
38
|
-
|
|
39
|
-
def call_event(self, event_name, args : list = []):
|
|
40
|
-
try:
|
|
41
|
-
if event_name in self._threaded_events:
|
|
42
|
-
for func in self._threaded_events[event_name]:
|
|
43
|
-
Thread(target=func, args=args).start()
|
|
44
|
-
if event_name in self._events:
|
|
45
|
-
for func in self._events[event_name]:
|
|
46
|
-
func(*args)
|
|
47
|
-
except Exception as e:
|
|
48
|
-
if self.ignore_exceptions:
|
|
49
|
-
print(
|
|
50
|
-
f"Warning: Caught error in event '{event_name}' - Full error below"
|
|
51
|
-
)
|
|
52
|
-
try:
|
|
53
|
-
traceback.print_exc()
|
|
54
|
-
except Exception:
|
|
55
|
-
print(e)
|
|
56
|
-
else:
|
|
57
|
-
raise(e)
|
|
58
|
-
|
|
59
|
-
@abstractmethod
|
|
60
|
-
def _updater(self):
|
|
61
|
-
pass
|
|
62
|
-
|
|
63
|
-
def stop(self):
|
|
64
|
-
"""
|
|
65
|
-
Permanently stops the event handler.
|
|
66
|
-
"""
|
|
67
|
-
self.running = False
|
|
68
|
-
if self._thread is not None:
|
|
69
|
-
self._thread = None
|
|
70
|
-
|
|
71
|
-
def pause(self):
|
|
72
|
-
"""
|
|
73
|
-
Pauses the event handler.
|
|
74
|
-
"""
|
|
75
|
-
self.running = False
|
|
76
|
-
|
|
77
|
-
def resume(self):
|
|
78
|
-
"""
|
|
79
|
-
Resumes the event handler.
|
|
80
|
-
"""
|
|
81
|
-
if self.running is False:
|
|
82
|
-
self.start()
|
|
83
|
-
|
|
84
|
-
def event(self, function=None, *, thread=False):
|
|
85
|
-
"""
|
|
86
|
-
Decorator function. Adds an event.
|
|
87
|
-
"""
|
|
88
|
-
def inner(function):
|
|
89
|
-
# called directly if the decorator provides arguments
|
|
90
|
-
if thread is True:
|
|
91
|
-
self._threaded_events[function.__name__].append(function)
|
|
92
|
-
else:
|
|
93
|
-
self._events[function.__name__].append(function)
|
|
94
|
-
|
|
95
|
-
if function is None:
|
|
96
|
-
# => the decorator provides arguments
|
|
97
|
-
return inner
|
|
98
|
-
else:
|
|
99
|
-
# => the decorator doesn't provide arguments
|
|
100
|
-
inner(function)
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
"""CloudEvents class"""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
from scratchattach.cloud import _base
|
|
5
|
-
from ._base import BaseEventHandler
|
|
6
|
-
from scratchattach.site import cloud_activity
|
|
7
|
-
import time
|
|
8
|
-
import json
|
|
9
|
-
from collections.abc import Iterator
|
|
10
|
-
|
|
11
|
-
class CloudEvents(BaseEventHandler):
|
|
12
|
-
"""
|
|
13
|
-
Class that calls events when on cloud updates that are received through a websocket connection.
|
|
14
|
-
"""
|
|
15
|
-
def __init__(self, cloud: _base.AnyCloud):
|
|
16
|
-
super().__init__()
|
|
17
|
-
self.cloud = cloud
|
|
18
|
-
self._session = cloud._session
|
|
19
|
-
self.source_stream = cloud.create_event_stream()
|
|
20
|
-
self.startup_time = time.time() * 1000
|
|
21
|
-
|
|
22
|
-
def disconnect(self):
|
|
23
|
-
self.source_stream.close()
|
|
24
|
-
|
|
25
|
-
def _updater(self):
|
|
26
|
-
"""
|
|
27
|
-
A process that listens for cloud activity and executes events on cloud activity
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
self.call_event("on_ready")
|
|
31
|
-
|
|
32
|
-
if self.running is False:
|
|
33
|
-
return
|
|
34
|
-
while True:
|
|
35
|
-
try:
|
|
36
|
-
while True:
|
|
37
|
-
for data in self.source_stream.read():
|
|
38
|
-
try:
|
|
39
|
-
_a = cloud_activity.CloudActivity(timestamp=time.time()*1000, _session=self._session, cloud=self.cloud)
|
|
40
|
-
if _a.timestamp < self.startup_time + 500: # catch the on_connect message sent by TurboWarp's (and sometimes Scratch's) cloud server
|
|
41
|
-
continue
|
|
42
|
-
data["variable_name"] = data["name"]
|
|
43
|
-
data["name"] = data["variable_name"].replace("☁ ", "")
|
|
44
|
-
_a._update_from_dict(data)
|
|
45
|
-
self.call_event("on_"+_a.type, [_a])
|
|
46
|
-
except Exception as e:
|
|
47
|
-
pass
|
|
48
|
-
except Exception:
|
|
49
|
-
print("CloudEvents: Disconnected. Reconnecting ...", time.time())
|
|
50
|
-
time.sleep(0.1) # cooldown
|
|
51
|
-
|
|
52
|
-
print("CloudEvents: Reconnected.", time.time())
|
|
53
|
-
self.call_event("on_reconnect", [])
|
|
54
|
-
|
|
55
|
-
class ManualCloudLogEvents:
|
|
56
|
-
"""
|
|
57
|
-
Class that calls events on cloud updates that are received from a clouddata log.
|
|
58
|
-
"""
|
|
59
|
-
def __init__(self, cloud: _base.LogCloud):
|
|
60
|
-
if not isinstance(cloud, _base.LogCloud):
|
|
61
|
-
raise ValueError("Cloud log events can't be used with a cloud that has no logs available")
|
|
62
|
-
self.cloud = cloud
|
|
63
|
-
self.source_cloud = cloud
|
|
64
|
-
self._session = cloud._session
|
|
65
|
-
self.last_timestamp = 0
|
|
66
|
-
|
|
67
|
-
def update(self) -> Iterator[tuple[str, list[cloud_activity.CloudActivity]]]:
|
|
68
|
-
"""
|
|
69
|
-
Update once and yield all packets
|
|
70
|
-
"""
|
|
71
|
-
try:
|
|
72
|
-
data = self.source_cloud.logs(limit=25)
|
|
73
|
-
for _a in data[::-1]:
|
|
74
|
-
if _a.timestamp <= self.last_timestamp:
|
|
75
|
-
continue
|
|
76
|
-
self.last_timestamp = _a.timestamp
|
|
77
|
-
yield ("on_"+_a.type, [_a])
|
|
78
|
-
except Exception:
|
|
79
|
-
pass
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class CloudLogEvents(BaseEventHandler):
|
|
83
|
-
"""
|
|
84
|
-
Class that calls events on cloud updates that are received from a clouddata log.
|
|
85
|
-
"""
|
|
86
|
-
def __init__(self, cloud: _base.LogCloud, *, update_interval=0.1):
|
|
87
|
-
super().__init__()
|
|
88
|
-
if not isinstance(cloud, _base.LogCloud):
|
|
89
|
-
raise ValueError("Cloud log events can't be used with a cloud that has no logs available")
|
|
90
|
-
self.cloud = cloud
|
|
91
|
-
self.source_cloud = cloud
|
|
92
|
-
self.update_interval = update_interval
|
|
93
|
-
self._session = cloud._session
|
|
94
|
-
self.last_timestamp = 0
|
|
95
|
-
self.manual_cloud_log_events = ManualCloudLogEvents(cloud)
|
|
96
|
-
|
|
97
|
-
def _updater(self):
|
|
98
|
-
logs = self.source_cloud.logs(limit=25)
|
|
99
|
-
self.last_timestamp = 0
|
|
100
|
-
if len(logs) != 0:
|
|
101
|
-
self.last_timestamp = logs[0].timestamp
|
|
102
|
-
|
|
103
|
-
self.call_event("on_ready")
|
|
104
|
-
|
|
105
|
-
while True:
|
|
106
|
-
if self.running is False:
|
|
107
|
-
return
|
|
108
|
-
for event_type, event_data in self.manual_cloud_log_events.update():
|
|
109
|
-
self.call_event(event_type, event_data)
|
|
110
|
-
time.sleep(self.update_interval)
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"""CloudRecorder class (used by ScratchCloud, TwCloud and other classes inheriting from BaseCloud to deliver cloud var values)"""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
from .cloud_events import CloudEvents
|
|
5
|
-
from typing import Optional
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class CloudRecorder(CloudEvents):
|
|
9
|
-
def __init__(self, cloud, *, initial_values: Optional[dict] = None):
|
|
10
|
-
if initial_values is None:
|
|
11
|
-
initial_values = {}
|
|
12
|
-
|
|
13
|
-
super().__init__(cloud)
|
|
14
|
-
self.cloud_values = initial_values
|
|
15
|
-
self.event(self.on_set)
|
|
16
|
-
|
|
17
|
-
def get_var(self, var):
|
|
18
|
-
if var not in self.cloud_values:
|
|
19
|
-
return None
|
|
20
|
-
return self.cloud_values[var]
|
|
21
|
-
|
|
22
|
-
def get_all_vars(self):
|
|
23
|
-
return self.cloud_values
|
|
24
|
-
|
|
25
|
-
def on_set(self, activity):
|
|
26
|
-
self.cloud_values[activity.var] = activity.value
|