scratchattach 2.1.15b0__py3-none-any.whl → 3.0.0b1__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.
- cli/__about__.py +1 -0
- cli/__init__.py +26 -0
- cli/cmd/__init__.py +4 -0
- cli/cmd/group.py +127 -0
- cli/cmd/login.py +60 -0
- cli/cmd/profile.py +7 -0
- cli/cmd/sessions.py +5 -0
- cli/context.py +142 -0
- cli/db.py +66 -0
- cli/namespace.py +14 -0
- {scratchattach/cloud → cloud}/_base.py +112 -87
- {scratchattach/cloud → cloud}/cloud.py +16 -16
- {scratchattach/editor → editor}/__init__.py +2 -1
- {scratchattach/editor → editor}/asset.py +26 -14
- {scratchattach/editor → editor}/backpack_json.py +3 -5
- {scratchattach/editor → editor}/base.py +2 -4
- {scratchattach/editor → editor}/block.py +27 -22
- {scratchattach/editor → editor}/blockshape.py +1 -1
- {scratchattach/editor → editor}/build_defaulting.py +2 -2
- editor/commons.py +145 -0
- {scratchattach/editor → editor}/field.py +1 -1
- {scratchattach/editor → editor}/inputs.py +6 -3
- {scratchattach/editor → editor}/meta.py +10 -7
- {scratchattach/editor → editor}/monitor.py +10 -8
- {scratchattach/editor → editor}/mutation.py +68 -11
- {scratchattach/editor → editor}/pallete.py +1 -3
- {scratchattach/editor → editor}/prim.py +4 -0
- {scratchattach/editor → editor}/project.py +118 -16
- {scratchattach/editor → editor}/sprite.py +25 -15
- {scratchattach/editor → editor}/vlb.py +2 -2
- {scratchattach/eventhandlers → eventhandlers}/_base.py +1 -0
- {scratchattach/eventhandlers → eventhandlers}/cloud_events.py +26 -6
- {scratchattach/eventhandlers → eventhandlers}/cloud_recorder.py +4 -4
- {scratchattach/eventhandlers → eventhandlers}/cloud_requests.py +139 -54
- {scratchattach/eventhandlers → eventhandlers}/cloud_server.py +6 -3
- {scratchattach/eventhandlers → eventhandlers}/cloud_storage.py +1 -2
- eventhandlers/filterbot.py +163 -0
- other/other_apis.py +598 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/METADATA +7 -11
- scratchattach-3.0.0b1.dist-info/RECORD +79 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/WHEEL +1 -1
- scratchattach-3.0.0b1.dist-info/entry_points.txt +2 -0
- scratchattach-3.0.0b1.dist-info/top_level.txt +7 -0
- {scratchattach/site → site}/_base.py +32 -5
- site/activity.py +426 -0
- {scratchattach/site → site}/alert.py +4 -5
- {scratchattach/site → site}/backpack_asset.py +2 -1
- {scratchattach/site → site}/classroom.py +80 -73
- {scratchattach/site → site}/cloud_activity.py +43 -29
- {scratchattach/site → site}/comment.py +86 -100
- {scratchattach/site → site}/forum.py +8 -4
- site/placeholder.py +132 -0
- {scratchattach/site → site}/project.py +228 -122
- {scratchattach/site → site}/session.py +156 -71
- {scratchattach/site → site}/studio.py +139 -46
- site/typed_dicts.py +151 -0
- {scratchattach/site → site}/user.py +511 -215
- {scratchattach/utils → utils}/commons.py +12 -4
- {scratchattach/utils → utils}/encoder.py +7 -4
- {scratchattach/utils → utils}/enums.py +1 -0
- {scratchattach/utils → utils}/exceptions.py +36 -2
- utils/optional_async.py +154 -0
- utils/requests.py +306 -0
- scratchattach/__init__.py +0 -29
- scratchattach/editor/commons.py +0 -273
- scratchattach/eventhandlers/filterbot.py +0 -161
- scratchattach/other/other_apis.py +0 -284
- scratchattach/site/activity.py +0 -382
- scratchattach/utils/requests.py +0 -93
- scratchattach-2.1.15b0.dist-info/RECORD +0 -66
- scratchattach-2.1.15b0.dist-info/top_level.txt +0 -1
- {scratchattach/cloud → cloud}/__init__.py +0 -0
- {scratchattach/editor → editor}/code_translation/__init__.py +0 -0
- {scratchattach/editor → editor}/code_translation/parse.py +0 -0
- {scratchattach/editor → editor}/comment.py +0 -0
- {scratchattach/editor → editor}/extension.py +0 -0
- {scratchattach/editor → editor}/twconfig.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/__init__.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/combine.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/message_events.py +0 -0
- {scratchattach/other → other}/__init__.py +0 -0
- {scratchattach/other → other}/project_json_capabilities.py +0 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
- {scratchattach/site → site}/__init__.py +0 -0
- {scratchattach/site → site}/browser_cookie3_stub.py +0 -0
- {scratchattach/site → site}/browser_cookies.py +0 -0
- {scratchattach/utils → utils}/__init__.py +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""ForumTopic and ForumPost classes"""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
import warnings
|
|
4
5
|
from dataclasses import dataclass, field
|
|
5
6
|
from typing import Optional, Any
|
|
6
7
|
from urllib.parse import urlparse, parse_qs
|
|
@@ -40,12 +41,15 @@ class ForumTopic(BaseSiteComponent):
|
|
|
40
41
|
'''
|
|
41
42
|
id: int
|
|
42
43
|
title: str
|
|
43
|
-
category_name: str
|
|
44
|
-
last_updated: str
|
|
44
|
+
category_name: Optional[str] = None
|
|
45
|
+
last_updated: Optional[str] = None
|
|
45
46
|
_session: Optional[module_session.Session] = field(default=None)
|
|
46
47
|
reply_count: Optional[int] = field(default=None)
|
|
47
48
|
view_count: Optional[int] = field(default=None)
|
|
48
49
|
|
|
50
|
+
def __str__(self):
|
|
51
|
+
return f"-F {self.title} ({self.id})"
|
|
52
|
+
|
|
49
53
|
def __post_init__(self):
|
|
50
54
|
# Info on how the .update method has to fetch the data:
|
|
51
55
|
self.update_function = requests.get
|
|
@@ -114,7 +118,7 @@ class ForumTopic(BaseSiteComponent):
|
|
|
114
118
|
list<scratchattach.forum.ForumPost>: A list containing the posts from the specified page of the forum topic
|
|
115
119
|
"""
|
|
116
120
|
if order != "oldest":
|
|
117
|
-
|
|
121
|
+
warnings.warn("Warning: All post orders except for 'oldest' are deprecated and no longer work") # For backwards compatibility
|
|
118
122
|
|
|
119
123
|
posts = []
|
|
120
124
|
|
|
@@ -143,7 +147,7 @@ class ForumTopic(BaseSiteComponent):
|
|
|
143
147
|
link = breadcrumb_ul.find_all('a')[1] # Get the right anchor tag
|
|
144
148
|
topic_category = link.text.strip() # Extract and strip text content
|
|
145
149
|
except Exception as e:
|
|
146
|
-
|
|
150
|
+
warnings.warn(f"Warning: Couldn't scrape topic category for topic {self.id} - {e}")
|
|
147
151
|
topic_category = ""
|
|
148
152
|
|
|
149
153
|
# get corresponding posts:
|
site/placeholder.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Classes and methods for interacting with turbowarp placeholder (https://share.turbowarp.org/)
|
|
2
|
+
import re
|
|
3
|
+
import bs4
|
|
4
|
+
import json
|
|
5
|
+
import io
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing_extensions import Optional
|
|
9
|
+
from bs4 import BeautifulSoup
|
|
10
|
+
|
|
11
|
+
from scratchattach.site import session
|
|
12
|
+
from scratchattach.site.typed_dicts import PlaceholderProjectDataDict
|
|
13
|
+
from scratchattach.utils.requests import requests
|
|
14
|
+
from scratchattach import editor
|
|
15
|
+
from scratchattach.utils import commons
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class PlaceholderProject:
|
|
20
|
+
id: str
|
|
21
|
+
|
|
22
|
+
title: Optional[str] = None
|
|
23
|
+
description: Optional[str] = None
|
|
24
|
+
md5exts_to_sha256: Optional[dict[str, str]] = None
|
|
25
|
+
admin_ownership_token: Optional[str] = None # guessing it's a str
|
|
26
|
+
|
|
27
|
+
_session: Optional[session.Session] = None
|
|
28
|
+
|
|
29
|
+
def get_json(self):
|
|
30
|
+
with requests.no_error_handling():
|
|
31
|
+
return requests.get(f"https://share.turbowarp.org/api/projects/{self.id}").json()
|
|
32
|
+
|
|
33
|
+
def update_by_html(self) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Scrape JS to update the project. Requires hjson
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
import hjson # type: ignore
|
|
39
|
+
except ImportError as e:
|
|
40
|
+
raise ImportError("Please use pip install hjson if you want to use placeholder projects!") from e
|
|
41
|
+
|
|
42
|
+
with requests.no_error_handling():
|
|
43
|
+
resp = requests.get(f"https://share.turbowarp.org/projects/{self.id}")
|
|
44
|
+
soup = BeautifulSoup(resp.text, "html.parser")
|
|
45
|
+
|
|
46
|
+
for script in soup.find_all("script"):
|
|
47
|
+
if not isinstance(script, bs4.element.Tag):
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
if raw_data := re.search("const data = \\[.*\"data\":{metadata:{.*},md5extsToSha256:.*];",
|
|
51
|
+
str(script.contents[0])):
|
|
52
|
+
data = raw_data.group().removeprefix("const data = ").removesuffix(";")
|
|
53
|
+
# this data is NOT json. Therefore, we can't just JSON.parse it.
|
|
54
|
+
# it's actually native JavaScript, but we can extract the information in a relatively stable way using hjson
|
|
55
|
+
# maybe, instead, a request should be made to GarboMuffin.
|
|
56
|
+
data = hjson.loads(data)
|
|
57
|
+
# i am unsure if the other data here is of any use. It may be artifacts coming from svelte
|
|
58
|
+
parsed_data: PlaceholderProjectDataDict = data[1]["data"]
|
|
59
|
+
|
|
60
|
+
self.title = parsed_data["metadata"]["title"]
|
|
61
|
+
self.description = parsed_data["metadata"]["description"]
|
|
62
|
+
self.md5exts_to_sha256 = dict(parsed_data["md5extsToSha256"])
|
|
63
|
+
self.admin_ownership_token = parsed_data["adminOwnershipToken"]
|
|
64
|
+
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
def get_project_body(self):
|
|
68
|
+
self.update_by_html()
|
|
69
|
+
|
|
70
|
+
data = self.get_json()
|
|
71
|
+
body = editor.Project.from_json(data)
|
|
72
|
+
body.name = self.title
|
|
73
|
+
|
|
74
|
+
for asset in body.assets:
|
|
75
|
+
table = self.md5exts_to_sha256
|
|
76
|
+
assert table is not None # this should never happen
|
|
77
|
+
data = get_asset(table[asset.md5ext])
|
|
78
|
+
asset.asset_file.data = data
|
|
79
|
+
|
|
80
|
+
return body
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_asset(sha256: str) -> bytes:
|
|
84
|
+
with requests.no_error_handling():
|
|
85
|
+
return requests.get(f"https://share.turbowarp.org/api/assets/{sha256}").content
|
|
86
|
+
|
|
87
|
+
def get_placeholder_project(_id: str):
|
|
88
|
+
return PlaceholderProject(_id)
|
|
89
|
+
|
|
90
|
+
def create_placeholder_project(title: str, data: bytes):
|
|
91
|
+
body = editor.Project.from_sb3(data)
|
|
92
|
+
|
|
93
|
+
asset_information: dict[str, dict[str, str | int]] = {}
|
|
94
|
+
for asset in body.assets:
|
|
95
|
+
print(asset)
|
|
96
|
+
print(asset.asset_file.sha256)
|
|
97
|
+
asset_information[asset.md5ext] = {
|
|
98
|
+
"sha256": asset.asset_file.sha256,
|
|
99
|
+
"size": len(asset.asset_file.data)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
print(f"{asset_information = }")
|
|
103
|
+
print(f"{body.name = }")
|
|
104
|
+
with requests.no_error_handling():
|
|
105
|
+
resp = requests.post("https://share.turbowarp.org/api/projects/new", data={
|
|
106
|
+
"title": title,
|
|
107
|
+
"assetInformation": asset_information,
|
|
108
|
+
}, files={
|
|
109
|
+
"project": ("blob", data, 'application/octet-stream'),
|
|
110
|
+
}, headers={
|
|
111
|
+
'accept': '*/*',
|
|
112
|
+
'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8',
|
|
113
|
+
# 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryYzpNqB5A2GEr99Vd',
|
|
114
|
+
'dnt': '1',
|
|
115
|
+
'origin': 'https://share.turbowarp.org',
|
|
116
|
+
'priority': 'u=1, i',
|
|
117
|
+
'referer': 'https://share.turbowarp.org/',
|
|
118
|
+
'sec-ch-ua': '"Google Chrome";v="141", "Not?A_Brand";v="8", "Chromium";v="141"',
|
|
119
|
+
'sec-ch-ua-mobile': '?0',
|
|
120
|
+
'sec-ch-ua-platform': '"Windows"',
|
|
121
|
+
'sec-fetch-dest': 'empty',
|
|
122
|
+
'sec-fetch-mode': 'cors',
|
|
123
|
+
'sec-fetch-site': 'same-origin',
|
|
124
|
+
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36'
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
print(resp, resp.content)
|
|
128
|
+
|
|
129
|
+
if __name__ == '__main__':
|
|
130
|
+
p = get_placeholder_project("44c35afc-fe00-49d8-afe7-d71f4430c121")
|
|
131
|
+
pb = p.get_project_body()
|
|
132
|
+
pb.export("test plac.sb3")
|