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.
Files changed (69) hide show
  1. scratchattach/__init__.py +14 -6
  2. scratchattach/__main__.py +93 -0
  3. {scratchattach-2.1.14.dist-info → scratchattach-3.0.0b0.dist-info}/METADATA +7 -11
  4. scratchattach-3.0.0b0.dist-info/RECORD +8 -0
  5. {scratchattach-2.1.14.dist-info → scratchattach-3.0.0b0.dist-info}/WHEEL +1 -1
  6. scratchattach-3.0.0b0.dist-info/entry_points.txt +2 -0
  7. scratchattach/cloud/__init__.py +0 -2
  8. scratchattach/cloud/_base.py +0 -458
  9. scratchattach/cloud/cloud.py +0 -183
  10. scratchattach/editor/__init__.py +0 -21
  11. scratchattach/editor/asset.py +0 -253
  12. scratchattach/editor/backpack_json.py +0 -117
  13. scratchattach/editor/base.py +0 -193
  14. scratchattach/editor/block.py +0 -579
  15. scratchattach/editor/blockshape.py +0 -357
  16. scratchattach/editor/build_defaulting.py +0 -51
  17. scratchattach/editor/code_translation/__init__.py +0 -0
  18. scratchattach/editor/code_translation/parse.py +0 -177
  19. scratchattach/editor/comment.py +0 -80
  20. scratchattach/editor/commons.py +0 -306
  21. scratchattach/editor/extension.py +0 -50
  22. scratchattach/editor/field.py +0 -99
  23. scratchattach/editor/inputs.py +0 -135
  24. scratchattach/editor/meta.py +0 -114
  25. scratchattach/editor/monitor.py +0 -183
  26. scratchattach/editor/mutation.py +0 -324
  27. scratchattach/editor/pallete.py +0 -90
  28. scratchattach/editor/prim.py +0 -170
  29. scratchattach/editor/project.py +0 -279
  30. scratchattach/editor/sprite.py +0 -599
  31. scratchattach/editor/twconfig.py +0 -114
  32. scratchattach/editor/vlb.py +0 -134
  33. scratchattach/eventhandlers/__init__.py +0 -0
  34. scratchattach/eventhandlers/_base.py +0 -100
  35. scratchattach/eventhandlers/cloud_events.py +0 -110
  36. scratchattach/eventhandlers/cloud_recorder.py +0 -26
  37. scratchattach/eventhandlers/cloud_requests.py +0 -459
  38. scratchattach/eventhandlers/cloud_server.py +0 -246
  39. scratchattach/eventhandlers/cloud_storage.py +0 -136
  40. scratchattach/eventhandlers/combine.py +0 -30
  41. scratchattach/eventhandlers/filterbot.py +0 -161
  42. scratchattach/eventhandlers/message_events.py +0 -42
  43. scratchattach/other/__init__.py +0 -0
  44. scratchattach/other/other_apis.py +0 -284
  45. scratchattach/other/project_json_capabilities.py +0 -475
  46. scratchattach/site/__init__.py +0 -0
  47. scratchattach/site/_base.py +0 -66
  48. scratchattach/site/activity.py +0 -382
  49. scratchattach/site/alert.py +0 -227
  50. scratchattach/site/backpack_asset.py +0 -118
  51. scratchattach/site/browser_cookie3_stub.py +0 -17
  52. scratchattach/site/browser_cookies.py +0 -61
  53. scratchattach/site/classroom.py +0 -447
  54. scratchattach/site/cloud_activity.py +0 -107
  55. scratchattach/site/comment.py +0 -242
  56. scratchattach/site/forum.py +0 -432
  57. scratchattach/site/project.py +0 -825
  58. scratchattach/site/session.py +0 -1238
  59. scratchattach/site/studio.py +0 -611
  60. scratchattach/site/user.py +0 -956
  61. scratchattach/utils/__init__.py +0 -0
  62. scratchattach/utils/commons.py +0 -255
  63. scratchattach/utils/encoder.py +0 -158
  64. scratchattach/utils/enums.py +0 -236
  65. scratchattach/utils/exceptions.py +0 -243
  66. scratchattach/utils/requests.py +0 -93
  67. scratchattach-2.1.14.dist-info/RECORD +0 -66
  68. {scratchattach-2.1.14.dist-info → scratchattach-3.0.0b0.dist-info}/licenses/LICENSE +0 -0
  69. {scratchattach-2.1.14.dist-info → scratchattach-3.0.0b0.dist-info}/top_level.txt +0 -0
@@ -1,170 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import warnings
4
- from dataclasses import dataclass
5
- from typing import Optional, Callable, Final
6
-
7
- from . import base, sprite, vlb, commons, build_defaulting
8
- from scratchattach.utils import enums, exceptions
9
-
10
-
11
- @dataclass
12
- class PrimType(base.JSONSerializable):
13
- code: int
14
- name: str
15
- attrs: list = None
16
- opcode: str = None
17
-
18
- def __eq__(self, other):
19
- if isinstance(other, str):
20
- return self.name == other
21
- elif isinstance(other, enums._EnumWrapper):
22
- other = other.value
23
- return super().__eq__(other)
24
-
25
- @staticmethod
26
- def from_json(data: int):
27
- return PrimTypes.find(data, "code")
28
-
29
- def to_json(self) -> int:
30
- return self.code
31
-
32
-
33
- BASIC_ATTRS: Final[tuple[str]] = ("value",)
34
- VLB_ATTRS: Final[tuple[str]] = ("name", "id", "x", "y")
35
-
36
-
37
- class PrimTypes(enums._EnumWrapper):
38
- # Yeah, they actually do have opcodes
39
- NUMBER = PrimType(4, "number", BASIC_ATTRS, "math_number")
40
- POSITIVE_NUMBER = PrimType(5, "positive number", BASIC_ATTRS, "math_positive_number")
41
- POSITIVE_INTEGER = PrimType(6, "positive integer", BASIC_ATTRS, "math_whole_number")
42
- INTEGER = PrimType(7, "integer", BASIC_ATTRS, "math_integer")
43
- ANGLE = PrimType(8, "angle", BASIC_ATTRS, "math_angle")
44
- COLOR = PrimType(9, "color", BASIC_ATTRS, "colour_picker")
45
- STRING = PrimType(10, "string", BASIC_ATTRS, "text")
46
- BROADCAST = PrimType(11, "broadcast", VLB_ATTRS, "event_broadcast_menu")
47
- VARIABLE = PrimType(12, "variable", VLB_ATTRS, "data_variable")
48
- LIST = PrimType(13, "list", VLB_ATTRS, "data_listcontents")
49
-
50
- @classmethod
51
- def find(cls, value, by: str, apply_func: Optional[Callable] = None) -> PrimType:
52
- return super().find(value, by, apply_func=apply_func)
53
-
54
-
55
- def is_prim_opcode(opcode: str):
56
- return opcode in PrimTypes.all_of("opcode") and opcode is not None
57
-
58
-
59
- class Prim(base.SpriteSubComponent):
60
- def __init__(self, _primtype: PrimType | PrimTypes, _value: Optional[str | vlb.Variable | vlb.List | vlb.Broadcast] = None,
61
- _name: Optional[str] = None, _id: Optional[str] = None, _x: Optional[int] = None,
62
- _y: Optional[int] = None, _sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
63
- """
64
- Class representing a Scratch string, number, angle, variable etc.
65
- Technically blocks but behave differently
66
- https://en.scratch-wiki.info/wiki/Scratch_File_Format#Targets:~:text=A%20few%20blocks,13
67
- """
68
- if isinstance(_primtype, PrimTypes):
69
- _primtype = _primtype.value
70
-
71
- self.type = _primtype
72
-
73
- self.value = _value
74
-
75
- self.name = _name
76
- """
77
- Once you get the object associated with this primitive (sprite.link_prims()),
78
- the name will be removed and the value will be changed from ``None``
79
- """
80
- self.value_id = _id
81
- """
82
- It's not an object accessed by id, but it may reference an object with an id.
83
-
84
- ----
85
-
86
- Once you get the object associated with it (sprite.link_prims()),
87
- the id will be removed and the value will be changed from ``None``
88
- """
89
-
90
- self.x = _x
91
- self.y = _y
92
-
93
- super().__init__(_sprite)
94
-
95
- def __repr__(self):
96
- if self.is_basic:
97
- return f"Prim<{self.type.name}: {self.value}>"
98
- elif self.is_vlb:
99
- return f"Prim<{self.type.name}: {self.value}>"
100
- else:
101
- return f"Prim<{self.type.name}>"
102
-
103
- @property
104
- def is_vlb(self):
105
- return self.type.attrs == VLB_ATTRS
106
-
107
- @property
108
- def is_basic(self):
109
- return self.type.attrs == BASIC_ATTRS
110
-
111
- @staticmethod
112
- def from_json(data: list):
113
- assert isinstance(data, list)
114
-
115
- _type_idx = data[0]
116
- _prim_type = PrimTypes.find(_type_idx, "code")
117
-
118
- _value, _name, _value_id, _x, _y = (None,) * 5
119
- if _prim_type.attrs == BASIC_ATTRS:
120
- assert len(data) == 2
121
- _value = data[1]
122
-
123
- elif _prim_type.attrs == VLB_ATTRS:
124
- assert len(data) in (3, 5)
125
- _name, _value_id = data[1:3]
126
-
127
- if len(data) == 5:
128
- _x, _y = data[3:]
129
-
130
- return Prim(_prim_type, _value, _name, _value_id, _x, _y)
131
-
132
- def to_json(self) -> list:
133
- if self.type.attrs == BASIC_ATTRS:
134
- return [self.type.code, self.value]
135
- else:
136
- return commons.trim_final_nones([self.type.code, self.value.name, self.value.id, self.x, self.y])
137
-
138
- def link_using_sprite(self):
139
- # Link prim to var/list/broadcast
140
- if self.is_vlb:
141
- if self.type.name == "variable":
142
- self.value = self.sprite.find_variable(self.value_id, "id")
143
-
144
- elif self.type.name == "list":
145
- self.value = self.sprite.find_list(self.value_id, "id")
146
-
147
- elif self.type.name == "broadcast":
148
- self.value = self.sprite.find_broadcast(self.value_id, "id")
149
- else:
150
- # This should never happen
151
- raise exceptions.BadVLBPrimitiveError(f"{self} claims to be VLB, but is {self.type.name}")
152
-
153
- if self.value is None:
154
- if not self.project:
155
- new_vlb = vlb.construct(self.type.name.lower(), self.value_id, self.name)
156
- self.sprite.add_local_global(new_vlb)
157
- self.value = new_vlb
158
-
159
- else:
160
- new_vlb = vlb.construct(self.type.name.lower(), self.value_id, self.name)
161
- self.sprite.stage.add_vlb(new_vlb)
162
-
163
- warnings.warn(
164
- f"Prim<name={self.name!r}, id={self.name!r}> has unknown {self.type.name} id; adding as global variable")
165
- self.name = None
166
- self.value_id = None
167
-
168
- @property
169
- def can_next(self):
170
- return False
@@ -1,279 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- import warnings
6
- from io import BytesIO, TextIOWrapper
7
- from typing import Optional, Iterable, Generator, BinaryIO
8
- from zipfile import ZipFile
9
-
10
- from . import base, meta, extension, monitor, sprite, asset, vlb, twconfig, comment, commons
11
- from scratchattach.site import session
12
- from scratchattach.site.project import get_project
13
- from scratchattach.utils import exceptions
14
-
15
-
16
- class Project(base.JSONExtractable):
17
- """
18
- sa.editor's equivalent of the ProjectBody. Represents the editor contents of a scratch project
19
- """
20
- def __init__(self, _name: Optional[str] = None, _meta: Optional[meta.Meta] = None, _extensions: Iterable[extension.Extension] = (),
21
- _monitors: Iterable[monitor.Monitor] = (), _sprites: Iterable[sprite.Sprite] = (), *,
22
- _asset_data: Optional[list[asset.AssetFile]] = None, _session: Optional[session.Session] = None):
23
- # Defaulting for list parameters
24
- if _meta is None:
25
- _meta = meta.Meta()
26
- if _asset_data is None:
27
- _asset_data = []
28
-
29
- self._session = _session
30
-
31
- self.name = _name
32
-
33
- self.meta = _meta
34
- self.extensions = _extensions
35
- self.monitors = list(_monitors)
36
- self.sprites = list(_sprites)
37
-
38
- self.asset_data = _asset_data
39
-
40
- self._tw_config_comment = None
41
-
42
- # Link subcomponents
43
- for iterable in (self.monitors, self.sprites):
44
- for _subcomponent in iterable:
45
- _subcomponent.project = self
46
-
47
- # Link sprites
48
- _stage_count = 0
49
-
50
- for _sprite in self.sprites:
51
- if _sprite.is_stage:
52
- _stage_count += 1
53
-
54
- _sprite.link_subcomponents()
55
-
56
- # Link monitors
57
- for _monitor in self.monitors:
58
- _monitor.link_using_project()
59
-
60
- if _stage_count != 1:
61
- raise exceptions.InvalidStageCount(f"Project {self}")
62
-
63
- def __repr__(self):
64
- _ret = "Project<"
65
- if self.name is not None:
66
- _ret += f"name={self.name}, "
67
- _ret += f"meta={self.meta}"
68
- _ret += '>'
69
- return _ret
70
-
71
- @property
72
- def stage(self) -> sprite.Sprite:
73
- for _sprite in self.sprites:
74
- if _sprite.is_stage:
75
- return _sprite
76
- warnings.warn(f"Could not find stage for {self.name}")
77
-
78
- def to_json(self) -> dict:
79
- _json = {
80
- "targets": [_sprite.to_json() for _sprite in self.sprites],
81
- "monitors": [_monitor.to_json() for _monitor in self.monitors],
82
- "extensions": [_extension.to_json() for _extension in self.extensions],
83
- "meta": self.meta.to_json(),
84
- }
85
-
86
- return _json
87
-
88
- @property
89
- def assets(self) -> Generator[asset.Asset, None, None]:
90
- for _sprite in self.sprites:
91
- for _asset in _sprite.assets:
92
- yield _asset
93
-
94
- @property
95
- def tw_config_comment(self) -> comment.Comment | None:
96
- for _comment in self.stage.comments:
97
- if twconfig.is_valid_twconfig(_comment.text):
98
- return _comment
99
- return None
100
-
101
- @property
102
- def tw_config(self) -> twconfig.TWConfig | None:
103
- _comment = self.tw_config_comment
104
- if _comment:
105
- return twconfig.TWConfig.from_str(_comment.text)
106
- return None
107
-
108
- @property
109
- def all_ids(self):
110
- _ret = []
111
- for _sprite in self.sprites:
112
- _ret += _sprite.all_ids
113
- return _ret
114
-
115
- @property
116
- def new_id(self):
117
- return commons.gen_id()
118
-
119
- @staticmethod
120
- def from_json(data: dict):
121
- assert isinstance(data, dict)
122
-
123
- # Load metadata
124
- _meta = meta.Meta.from_json(data.get("meta"))
125
-
126
- # Load extensions
127
- _extensions = []
128
- for _extension_data in data.get("extensions", []):
129
- _extensions.append(extension.Extension.from_json(_extension_data))
130
-
131
- # Load monitors
132
- _monitors = []
133
- for _monitor_data in data.get("monitors", []):
134
- _monitors.append(monitor.Monitor.from_json(_monitor_data))
135
-
136
- # Load sprites (targets)
137
- _sprites = []
138
- for _sprite_data in data.get("targets", []):
139
- _sprites.append(sprite.Sprite.from_json(_sprite_data))
140
-
141
- return Project(None, _meta, _extensions, _monitors, _sprites)
142
-
143
- @staticmethod
144
- def load_json(data: str | bytes | TextIOWrapper | BinaryIO, load_assets: bool = True, _name: Optional[str] = None):
145
- """
146
- Load project JSON and assets from an .sb3 file/bytes/file path
147
- :return: Project name, asset data, json string
148
- """
149
- _dir_for_name = None
150
-
151
- if _name is None:
152
- if hasattr(data, "name"):
153
- _dir_for_name = data.name
154
-
155
- if not isinstance(_name, str) and _name is not None:
156
- _name = str(_name)
157
-
158
- if isinstance(data, bytes):
159
- data = BytesIO(data)
160
-
161
- elif isinstance(data, str):
162
- _dir_for_name = data
163
- data = open(data, "rb")
164
-
165
- if _name is None and _dir_for_name is not None:
166
- # Remove any directory names and the file extension
167
- _name = _dir_for_name.split('/')[-1]
168
- _name = '.'.join(_name.split('.')[:-1])
169
-
170
- asset_data = []
171
- with data:
172
- # For if the sb3 is just JSON (e.g. if it's exported from scratchattach)
173
- if commons.is_valid_json(data):
174
- json_str = data
175
-
176
- else:
177
- with ZipFile(data) as archive:
178
- json_str = archive.read("project.json")
179
-
180
- # Also load assets
181
- if load_assets:
182
- for filename in archive.namelist():
183
- if filename != "project.json":
184
- md5_hash = filename.split('.')[0]
185
-
186
- asset_data.append(
187
- asset.AssetFile(filename, archive.read(filename), md5_hash)
188
- )
189
-
190
- else:
191
- warnings.warn(
192
- "Loading sb3 without loading assets. When exporting the project, there may be errors due to assets not being uploaded to the Scratch website")
193
-
194
- return _name, asset_data, json_str
195
-
196
- @classmethod
197
- def from_sb3(cls, data: str | bytes | TextIOWrapper | BinaryIO, load_assets: bool = True, _name: Optional[str] = None):
198
- """
199
- Load a project from an .sb3 file/bytes/file path
200
- """
201
- _name, asset_data, json_str = cls.load_json(data, load_assets, _name)
202
- data = json.loads(json_str)
203
-
204
- project = cls.from_json(data)
205
- project.name = _name
206
- project.asset_data = asset_data
207
-
208
- return project
209
-
210
- @staticmethod
211
- def from_id(project_id: int, _name: Optional[str] = None):
212
- _proj = get_project(project_id)
213
- data = json.loads(_proj.get_json())
214
-
215
- if _name is None:
216
- _name = _proj.title
217
- _name = str(_name)
218
-
219
- _proj = Project.from_json(data)
220
- _proj.name = _name
221
- return _proj
222
-
223
- def find_vlb(self, value: str | None, by: str = "name",
224
- multiple: bool = False) -> vlb.Variable | vlb.List | vlb.Broadcast | list[
225
- vlb.Variable | vlb.List | vlb.Broadcast]:
226
- _ret = []
227
- for _sprite in self.sprites:
228
- val = _sprite.find_vlb(value, by, multiple)
229
- if multiple:
230
- _ret += val
231
- else:
232
- if val is not None:
233
- return val
234
- if multiple:
235
- return _ret
236
-
237
- def find_sprite(self, value: str | None, by: str = "name",
238
- multiple: bool = False) -> sprite.Sprite | list[sprite.Sprite]:
239
- _ret = []
240
- for _sprite in self.sprites:
241
- if by == "name":
242
- _val = _sprite.name
243
- else:
244
- _val = getattr(_sprite, by)
245
-
246
- if _val == value:
247
- if multiple:
248
- _ret.append(_sprite)
249
- else:
250
- return _sprite
251
-
252
- if multiple:
253
- return _ret
254
-
255
- def export(self, fp: str, *, auto_open: bool = False, export_as_zip: bool = True):
256
- data = self.to_json()
257
-
258
- if export_as_zip:
259
- with ZipFile(fp, "w") as archive:
260
- for _asset in self.assets:
261
- asset_file = _asset.asset_file
262
- if asset_file.filename not in archive.namelist():
263
- archive.writestr(asset_file.filename, asset_file.data)
264
-
265
- archive.writestr("project.json", json.dumps(data))
266
- else:
267
- with open(fp, "w") as json_file:
268
- json.dump(data, json_file)
269
-
270
- if auto_open:
271
- os.system(f"explorer.exe \"{fp}\"")
272
-
273
- def add_monitor(self, _monitor: monitor.Monitor) -> monitor.Monitor:
274
- """
275
- Bind a monitor to this project. Doing these manually can lead to interesting results.
276
- """
277
- _monitor.project = self
278
- _monitor.reporter_id = self.new_id
279
- self.monitors.append(_monitor)