scratchattach 2.1.15b0__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.15b0.dist-info → scratchattach-3.0.0b0.dist-info}/METADATA +7 -11
- scratchattach-3.0.0b0.dist-info/RECORD +8 -0
- {scratchattach-2.1.15b0.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 -273
- 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 -826
- 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.15b0.dist-info/RECORD +0 -66
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b0.dist-info}/licenses/LICENSE +0 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b0.dist-info}/top_level.txt +0 -0
scratchattach/utils/__init__.py
DELETED
|
File without changes
|
scratchattach/utils/commons.py
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
"""v2 ready: Common functions used by various internal modules"""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import string
|
|
5
|
-
|
|
6
|
-
from typing import Optional, Final, Any, TypeVar, Callable, TYPE_CHECKING, Union
|
|
7
|
-
from threading import Event as ManualResetEvent
|
|
8
|
-
from threading import Lock
|
|
9
|
-
|
|
10
|
-
from . import exceptions
|
|
11
|
-
from .requests import requests
|
|
12
|
-
|
|
13
|
-
from scratchattach.site import _base
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
headers: Final = {
|
|
17
|
-
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
18
|
-
"(KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36",
|
|
19
|
-
"x-csrftoken": "a",
|
|
20
|
-
"x-requested-with": "XMLHttpRequest",
|
|
21
|
-
"referer": "https://scratch.mit.edu",
|
|
22
|
-
}
|
|
23
|
-
empty_project_json: Final = {
|
|
24
|
-
'targets': [
|
|
25
|
-
{
|
|
26
|
-
'isStage': True,
|
|
27
|
-
'name': 'Stage',
|
|
28
|
-
'variables': {
|
|
29
|
-
'`jEk@4|i[#Fk?(8x)AV.-my variable': [
|
|
30
|
-
'my variable',
|
|
31
|
-
0,
|
|
32
|
-
],
|
|
33
|
-
},
|
|
34
|
-
'lists': {},
|
|
35
|
-
'broadcasts': {},
|
|
36
|
-
'blocks': {},
|
|
37
|
-
'comments': {},
|
|
38
|
-
'currentCostume': 0,
|
|
39
|
-
'costumes': [
|
|
40
|
-
{
|
|
41
|
-
'name': '',
|
|
42
|
-
'bitmapResolution': 1,
|
|
43
|
-
'dataFormat': 'svg',
|
|
44
|
-
'assetId': '14e46ec3e2ba471c2adfe8f119052307',
|
|
45
|
-
'md5ext': '14e46ec3e2ba471c2adfe8f119052307.svg',
|
|
46
|
-
'rotationCenterX': 0,
|
|
47
|
-
'rotationCenterY': 0,
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
'sounds': [],
|
|
51
|
-
'volume': 100,
|
|
52
|
-
'layerOrder': 0,
|
|
53
|
-
'tempo': 60,
|
|
54
|
-
'videoTransparency': 50,
|
|
55
|
-
'videoState': 'on',
|
|
56
|
-
'textToSpeechLanguage': None,
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
'monitors': [],
|
|
60
|
-
'extensions': [],
|
|
61
|
-
'meta': {
|
|
62
|
-
'semver': '3.0.0',
|
|
63
|
-
'vm': '2.3.0',
|
|
64
|
-
'agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
|
|
65
|
-
'Chrome/124.0.0.0 Safari/537.36',
|
|
66
|
-
},
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def api_iterative_data(fetch_func: Callable[[int, int], list], limit: int, offset: int, max_req_limit: int = 40,
|
|
71
|
-
unpack: bool = True) -> list:
|
|
72
|
-
"""
|
|
73
|
-
Iteratively gets data by calling fetch_func with a moving offset and a limit.
|
|
74
|
-
Once fetch_func returns None, the retrieval is completed.
|
|
75
|
-
"""
|
|
76
|
-
if limit is None:
|
|
77
|
-
limit = max_req_limit
|
|
78
|
-
|
|
79
|
-
end = offset + limit
|
|
80
|
-
api_data = []
|
|
81
|
-
for offs in range(offset, end, max_req_limit):
|
|
82
|
-
# Mimic actual scratch by only requesting the max amount
|
|
83
|
-
data = fetch_func(offs, max_req_limit)
|
|
84
|
-
if data is None:
|
|
85
|
-
break
|
|
86
|
-
|
|
87
|
-
if unpack:
|
|
88
|
-
api_data.extend(data)
|
|
89
|
-
else:
|
|
90
|
-
api_data.append(data)
|
|
91
|
-
|
|
92
|
-
if len(data) < max_req_limit:
|
|
93
|
-
break
|
|
94
|
-
|
|
95
|
-
api_data = api_data[:limit]
|
|
96
|
-
return api_data
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def api_iterative(url: str, *, limit: int, offset: int, max_req_limit: int = 40, add_params: str = "",
|
|
100
|
-
_headers: Optional[dict] = None, cookies: Optional[dict] = None):
|
|
101
|
-
"""
|
|
102
|
-
Function for getting data from one of Scratch's iterative JSON API endpoints (like /users/<user>/followers, or /users/<user>/projects)
|
|
103
|
-
"""
|
|
104
|
-
if _headers is None:
|
|
105
|
-
_headers = headers.copy()
|
|
106
|
-
if cookies is None:
|
|
107
|
-
cookies = {}
|
|
108
|
-
|
|
109
|
-
if offset < 0:
|
|
110
|
-
raise exceptions.BadRequest("offset parameter must be >= 0")
|
|
111
|
-
if limit < 0:
|
|
112
|
-
raise exceptions.BadRequest("limit parameter must be >= 0")
|
|
113
|
-
|
|
114
|
-
def fetch(off: int, lim: int):
|
|
115
|
-
"""
|
|
116
|
-
Performs a single API request
|
|
117
|
-
"""
|
|
118
|
-
resp = requests.get(
|
|
119
|
-
f"{url}?limit={lim}&offset={off}{add_params}", headers=_headers, cookies=cookies, timeout=10
|
|
120
|
-
).json()
|
|
121
|
-
|
|
122
|
-
if not resp:
|
|
123
|
-
return None
|
|
124
|
-
if resp == {"code": "BadRequest", "message": ""}:
|
|
125
|
-
raise exceptions.BadRequest("The passed arguments are invalid")
|
|
126
|
-
return resp
|
|
127
|
-
|
|
128
|
-
api_data = api_iterative_data(
|
|
129
|
-
fetch, limit, offset, max_req_limit=max_req_limit, unpack=True
|
|
130
|
-
)
|
|
131
|
-
return api_data
|
|
132
|
-
|
|
133
|
-
def _get_object(identificator_name, identificator, __class: type[C], NotFoundException, session=None) -> C:
|
|
134
|
-
# Internal function: Generalization of the process ran by get_user, get_studio etc.
|
|
135
|
-
# Builds an object of class that is inheriting from BaseSiteComponent
|
|
136
|
-
# # Class must inherit from BaseSiteComponent
|
|
137
|
-
from scratchattach.site import project
|
|
138
|
-
try:
|
|
139
|
-
use_class: type = __class
|
|
140
|
-
if __class is project.PartialProject:
|
|
141
|
-
use_class = project.Project
|
|
142
|
-
assert issubclass(use_class, __class)
|
|
143
|
-
_object = use_class(**{identificator_name: identificator, "_session": session})
|
|
144
|
-
r = _object.update()
|
|
145
|
-
if r == "429":
|
|
146
|
-
raise exceptions.Response429(
|
|
147
|
-
"Your network is blocked or rate-limited by Scratch.\n"
|
|
148
|
-
"If you're using an online IDE like replit.com, try running the code on your computer.")
|
|
149
|
-
if not r:
|
|
150
|
-
# Target is unshared. The cases that this can happen in are hardcoded:
|
|
151
|
-
if __class is project.PartialProject: # Case: Target is an unshared project.
|
|
152
|
-
_object = project.PartialProject(**{identificator_name: identificator,
|
|
153
|
-
"shared": False, "_session": session})
|
|
154
|
-
assert isinstance(_object, __class)
|
|
155
|
-
return _object
|
|
156
|
-
else:
|
|
157
|
-
raise NotFoundException
|
|
158
|
-
else:
|
|
159
|
-
return _object
|
|
160
|
-
except KeyError as e:
|
|
161
|
-
raise NotFoundException(f"Key error at key {e} when reading API response")
|
|
162
|
-
except Exception as e:
|
|
163
|
-
raise e
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def webscrape_count(raw, text_before, text_after, cls: type = int) -> int | Any:
|
|
167
|
-
return cls(raw.split(text_before)[1].split(text_after)[0])
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if TYPE_CHECKING:
|
|
171
|
-
C = TypeVar("C", bound=_base.BaseSiteComponent)
|
|
172
|
-
|
|
173
|
-
def parse_object_list(raw, __class: type[C], session=None, primary_key="id") -> list[C]:
|
|
174
|
-
results = []
|
|
175
|
-
for raw_dict in raw:
|
|
176
|
-
try:
|
|
177
|
-
_obj = __class(**{primary_key: raw_dict[primary_key], "_session": session})
|
|
178
|
-
_obj._update_from_dict(raw_dict)
|
|
179
|
-
results.append(_obj)
|
|
180
|
-
except Exception as e:
|
|
181
|
-
print("Warning raised by scratchattach: failed to parse ", raw_dict, "error", e)
|
|
182
|
-
return results
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
class LockEvent:
|
|
186
|
-
"""
|
|
187
|
-
Can be waited on and triggered. Not to be confused with threading.Event, which has to be reset.
|
|
188
|
-
"""
|
|
189
|
-
_event: ManualResetEvent
|
|
190
|
-
_locks: list[Lock]
|
|
191
|
-
_access_locks: Lock
|
|
192
|
-
def __init__(self):
|
|
193
|
-
self._event = ManualResetEvent()
|
|
194
|
-
self._locks = []
|
|
195
|
-
self._access_locks = Lock()
|
|
196
|
-
|
|
197
|
-
def wait(self, blocking: bool = True, timeout: Optional[Union[int, float]] = None) -> bool:
|
|
198
|
-
"""
|
|
199
|
-
Wait for the event.
|
|
200
|
-
"""
|
|
201
|
-
return self._event.wait(timeout if blocking else 0)
|
|
202
|
-
|
|
203
|
-
def trigger(self):
|
|
204
|
-
"""
|
|
205
|
-
Trigger all threads waiting on this event to continue.
|
|
206
|
-
"""
|
|
207
|
-
with self._access_locks:
|
|
208
|
-
for lock in self._locks:
|
|
209
|
-
try:
|
|
210
|
-
lock.release()
|
|
211
|
-
except RuntimeError:
|
|
212
|
-
pass
|
|
213
|
-
self._locks.clear()
|
|
214
|
-
self._event.set()
|
|
215
|
-
self._event = ManualResetEvent()
|
|
216
|
-
|
|
217
|
-
def on(self) -> Lock:
|
|
218
|
-
"""
|
|
219
|
-
Return a lock that will unlock once the event takes place. Return value has to be waited on to wait for the event.
|
|
220
|
-
"""
|
|
221
|
-
lock = Lock()
|
|
222
|
-
with self._access_locks:
|
|
223
|
-
self._locks.append(lock)
|
|
224
|
-
lock.acquire(timeout=0)
|
|
225
|
-
return lock
|
|
226
|
-
|
|
227
|
-
def get_class_sort_mode(mode: str) -> tuple[str, str]:
|
|
228
|
-
"""
|
|
229
|
-
Returns the sort mode for the given mode for classes only
|
|
230
|
-
"""
|
|
231
|
-
ascsort = ''
|
|
232
|
-
descsort = ''
|
|
233
|
-
|
|
234
|
-
mode = mode.lower()
|
|
235
|
-
if mode == "last created":
|
|
236
|
-
pass
|
|
237
|
-
elif mode == "students":
|
|
238
|
-
descsort = "student_count"
|
|
239
|
-
elif mode == "a-z":
|
|
240
|
-
ascsort = "title"
|
|
241
|
-
elif mode == "z-a":
|
|
242
|
-
descsort = "title"
|
|
243
|
-
|
|
244
|
-
return ascsort, descsort
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
def b62_decode(s: str):
|
|
248
|
-
chars = string.digits + string.ascii_uppercase + string.ascii_lowercase
|
|
249
|
-
|
|
250
|
-
ret = 0
|
|
251
|
-
for char in s:
|
|
252
|
-
ret = ret * 62 + chars.index(char)
|
|
253
|
-
|
|
254
|
-
return ret
|
|
255
|
-
|
scratchattach/utils/encoder.py
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import math
|
|
3
|
-
from . import exceptions
|
|
4
|
-
|
|
5
|
-
letters = [
|
|
6
|
-
None,
|
|
7
|
-
None,
|
|
8
|
-
None,
|
|
9
|
-
None,
|
|
10
|
-
None,
|
|
11
|
-
None,
|
|
12
|
-
None,
|
|
13
|
-
None,
|
|
14
|
-
None,
|
|
15
|
-
None,
|
|
16
|
-
"1",
|
|
17
|
-
"2",
|
|
18
|
-
"3",
|
|
19
|
-
"4",
|
|
20
|
-
"5",
|
|
21
|
-
"6",
|
|
22
|
-
"7",
|
|
23
|
-
"8",
|
|
24
|
-
"9",
|
|
25
|
-
"0",
|
|
26
|
-
" ",
|
|
27
|
-
"a",
|
|
28
|
-
"A",
|
|
29
|
-
"b",
|
|
30
|
-
"B",
|
|
31
|
-
"c",
|
|
32
|
-
"C",
|
|
33
|
-
"d",
|
|
34
|
-
"D",
|
|
35
|
-
"e",
|
|
36
|
-
"E",
|
|
37
|
-
"f",
|
|
38
|
-
"F",
|
|
39
|
-
"g",
|
|
40
|
-
"G",
|
|
41
|
-
"h",
|
|
42
|
-
"H",
|
|
43
|
-
"i",
|
|
44
|
-
"I",
|
|
45
|
-
"j",
|
|
46
|
-
"J",
|
|
47
|
-
"k",
|
|
48
|
-
"K",
|
|
49
|
-
"l",
|
|
50
|
-
"L",
|
|
51
|
-
"m",
|
|
52
|
-
"M",
|
|
53
|
-
"n",
|
|
54
|
-
"N",
|
|
55
|
-
"o",
|
|
56
|
-
"O",
|
|
57
|
-
"p",
|
|
58
|
-
"P",
|
|
59
|
-
"q",
|
|
60
|
-
"Q",
|
|
61
|
-
"r",
|
|
62
|
-
"R",
|
|
63
|
-
"s",
|
|
64
|
-
"S",
|
|
65
|
-
"t",
|
|
66
|
-
"T",
|
|
67
|
-
"u",
|
|
68
|
-
"U",
|
|
69
|
-
"v",
|
|
70
|
-
"V",
|
|
71
|
-
"w",
|
|
72
|
-
"W",
|
|
73
|
-
"x",
|
|
74
|
-
"X",
|
|
75
|
-
"y",
|
|
76
|
-
"Y",
|
|
77
|
-
"z",
|
|
78
|
-
"Z",
|
|
79
|
-
"*",
|
|
80
|
-
"/",
|
|
81
|
-
".",
|
|
82
|
-
",",
|
|
83
|
-
"!",
|
|
84
|
-
'"',
|
|
85
|
-
"§",
|
|
86
|
-
"$",
|
|
87
|
-
"%",
|
|
88
|
-
"_",
|
|
89
|
-
"-",
|
|
90
|
-
"(",
|
|
91
|
-
"´",
|
|
92
|
-
")",
|
|
93
|
-
"`",
|
|
94
|
-
"?",
|
|
95
|
-
"new line",
|
|
96
|
-
"@",
|
|
97
|
-
"#",
|
|
98
|
-
"~",
|
|
99
|
-
";",
|
|
100
|
-
":",
|
|
101
|
-
"+",
|
|
102
|
-
"&",
|
|
103
|
-
"|",
|
|
104
|
-
"^",
|
|
105
|
-
"'"
|
|
106
|
-
]
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
class Encoding:
|
|
110
|
-
"""
|
|
111
|
-
Class that contains tools for encoding / decoding strings. The strings encoded / decoded with these functions can be decoded / encoded with Scratch using this sprite: https://scratch3-assets.1tim.repl.co/Encoder.sprite3
|
|
112
|
-
"""
|
|
113
|
-
@staticmethod
|
|
114
|
-
def decode(inp):
|
|
115
|
-
"""
|
|
116
|
-
Args:
|
|
117
|
-
inp (str): The encoded input.
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
str: The decoded output.
|
|
121
|
-
"""
|
|
122
|
-
try:
|
|
123
|
-
inp = str(inp)
|
|
124
|
-
except Exception:
|
|
125
|
-
raise(exceptions.InvalidDecodeInput)
|
|
126
|
-
outp = ""
|
|
127
|
-
for i in range(0, math.floor(len(inp) / 2)):
|
|
128
|
-
letter = letters[int(f"{inp[i*2]}{inp[(i*2)+1]}")]
|
|
129
|
-
outp = f"{outp}{letter}"
|
|
130
|
-
return outp
|
|
131
|
-
|
|
132
|
-
@staticmethod
|
|
133
|
-
def encode(inp):
|
|
134
|
-
"""
|
|
135
|
-
Args:
|
|
136
|
-
inp (str): The decoded input.
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
str: The encoded output.
|
|
140
|
-
"""
|
|
141
|
-
inp = str(inp)
|
|
142
|
-
outp = ""
|
|
143
|
-
for i in inp:
|
|
144
|
-
if i in letters:
|
|
145
|
-
outp = f"{outp}{letters.index(i)}"
|
|
146
|
-
else:
|
|
147
|
-
outp += str(letters.index(" "))
|
|
148
|
-
return outp
|
|
149
|
-
|
|
150
|
-
@staticmethod
|
|
151
|
-
def replace_char(old_char, new_char):
|
|
152
|
-
"""
|
|
153
|
-
Replaces a character in the list that the encoder uses to encode / decode values.
|
|
154
|
-
You can access this list using `scratchattach.encoder.letters`
|
|
155
|
-
"""
|
|
156
|
-
i = letters.index(old_char)
|
|
157
|
-
letters[i] = new_char
|
|
158
|
-
|
scratchattach/utils/enums.py
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
List of supported languages of scratch's translate and text2speech extensions.
|
|
3
|
-
Adapted from https://translate-service.scratch.mit.edu/supported?language=en
|
|
4
|
-
"""
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from enum import Enum
|
|
9
|
-
from typing import Optional, Callable, Iterable
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class Language:
|
|
14
|
-
name: str = None
|
|
15
|
-
code: str = None
|
|
16
|
-
locales: list[str] = None
|
|
17
|
-
tts_locale: str = None
|
|
18
|
-
single_gender: bool = None
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class _EnumWrapper(Enum):
|
|
22
|
-
@classmethod
|
|
23
|
-
def find(cls, value, by: str, apply_func: Optional[Callable] = None):
|
|
24
|
-
"""
|
|
25
|
-
Finds the enum item with the given attribute that is equal to the given value.
|
|
26
|
-
the apply_func will be applied to the attribute of each language object before comparison.
|
|
27
|
-
|
|
28
|
-
i.e. Languages.find("ukranian", "name", str.lower) will return the Ukrainian language dataclass object
|
|
29
|
-
(even though Ukrainian was spelt lowercase, since str.lower will convert the "Ukrainian" string to lowercase)
|
|
30
|
-
"""
|
|
31
|
-
if apply_func is None:
|
|
32
|
-
def apply_func(x):
|
|
33
|
-
return x
|
|
34
|
-
|
|
35
|
-
for item in cls:
|
|
36
|
-
item_obj = item.value
|
|
37
|
-
|
|
38
|
-
try:
|
|
39
|
-
if by is None:
|
|
40
|
-
_val = item_obj
|
|
41
|
-
else:
|
|
42
|
-
_val = getattr(item_obj, by)
|
|
43
|
-
|
|
44
|
-
if apply_func(_val) == value:
|
|
45
|
-
return item_obj
|
|
46
|
-
|
|
47
|
-
except TypeError:
|
|
48
|
-
pass
|
|
49
|
-
|
|
50
|
-
@classmethod
|
|
51
|
-
def all_of(cls, attr_name: str, apply_func: Optional[Callable] = None) -> Iterable:
|
|
52
|
-
"""
|
|
53
|
-
Returns the list of each listed enum item's specified attribute by "attr_name"
|
|
54
|
-
|
|
55
|
-
i.e. Languages.all_of("name") will return a list of names:
|
|
56
|
-
["Albanian", "Amharic", ...]
|
|
57
|
-
|
|
58
|
-
The apply_func function will be applied to every list item,
|
|
59
|
-
i.e. Languages.all_of("name", str.lower) will return the same except in lowercase:
|
|
60
|
-
["albanian", "amharic", ...]
|
|
61
|
-
"""
|
|
62
|
-
if apply_func is None:
|
|
63
|
-
def apply_func(x):
|
|
64
|
-
return x
|
|
65
|
-
|
|
66
|
-
for item in cls:
|
|
67
|
-
item_obj = item.value
|
|
68
|
-
attr = getattr(item_obj, attr_name)
|
|
69
|
-
try:
|
|
70
|
-
yield apply_func(attr)
|
|
71
|
-
|
|
72
|
-
except TypeError:
|
|
73
|
-
yield attr
|
|
74
|
-
|
|
75
|
-
@classmethod
|
|
76
|
-
def find_by_attrs(cls, value, bys: list[str], apply_func: Optional[Callable] = None) -> list:
|
|
77
|
-
"""
|
|
78
|
-
Calls the EnumWrapper.by function multiple times until a match is found, using the provided 'by' attribute names
|
|
79
|
-
"""
|
|
80
|
-
for by in bys:
|
|
81
|
-
ret = cls.find(value, by, apply_func)
|
|
82
|
-
if ret is not None:
|
|
83
|
-
return ret
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class Languages(_EnumWrapper):
|
|
87
|
-
Albanian = Language('Albanian', 'sq', None, None, None)
|
|
88
|
-
Amharic = Language('Amharic', 'am', None, None, None)
|
|
89
|
-
Arabic = Language('Arabic', 'ar', ['ar'], 'arb', True)
|
|
90
|
-
Armenian = Language('Armenian', 'hy', None, None, None)
|
|
91
|
-
Azerbaijani = Language('Azerbaijani', 'az', None, None, None)
|
|
92
|
-
Basque = Language('Basque', 'eu', None, None, None)
|
|
93
|
-
Belarusian = Language('Belarusian', 'be', None, None, None)
|
|
94
|
-
Bulgarian = Language('Bulgarian', 'bg', None, None, None)
|
|
95
|
-
Catalan = Language('Catalan', 'ca', None, None, None)
|
|
96
|
-
Chinese_Traditional = Language('Chinese (Traditional)', 'zh-tw', ['zh-cn', 'zh-tw'], 'cmn-CN', True)
|
|
97
|
-
Croatian = Language('Croatian', 'hr', None, None, None)
|
|
98
|
-
Czech = Language('Czech', 'cs', None, None, None)
|
|
99
|
-
Danish = Language('Danish', 'da', ['da'], 'da-DK', False)
|
|
100
|
-
Dutch = Language('Dutch', 'nl', ['nl'], 'nl-NL', False)
|
|
101
|
-
English = Language('English', 'en', ['en'], 'en-US', False)
|
|
102
|
-
Esperanto = Language('Esperanto', 'eo', None, None, None)
|
|
103
|
-
Estonian = Language('Estonian', 'et', None, None, None)
|
|
104
|
-
Finnish = Language('Finnish', 'fi', None, None, None)
|
|
105
|
-
French = Language('French', 'fr', ['fr'], 'fr-FR', False)
|
|
106
|
-
Galician = Language('Galician', 'gl', None, None, None)
|
|
107
|
-
German = Language('German', 'de', ['de'], 'de-DE', False)
|
|
108
|
-
Greek = Language('Greek', 'el', None, None, None)
|
|
109
|
-
Haitian_Creole = Language('Haitian Creole', 'ht', None, None, None)
|
|
110
|
-
Hindi = Language('Hindi', 'hi', ['hi'], 'hi-IN', True)
|
|
111
|
-
Hungarian = Language('Hungarian', 'hu', None, None, None)
|
|
112
|
-
Icelandic = Language('Icelandic', 'is', ['is'], 'is-IS', False)
|
|
113
|
-
Indonesian = Language('Indonesian', 'id', None, None, None)
|
|
114
|
-
Irish = Language('Irish', 'ga', None, None, None)
|
|
115
|
-
Italian = Language('Italian', 'it', ['it'], 'it-IT', False)
|
|
116
|
-
Japanese = Language('Japanese', 'ja', ['ja', 'ja-hira'], 'ja-JP', False)
|
|
117
|
-
Kannada = Language('Kannada', 'kn', None, None, None)
|
|
118
|
-
Korean = Language('Korean', 'ko', ['ko'], 'ko-KR', True)
|
|
119
|
-
Kurdish_Kurmanji = Language('Kurdish (Kurmanji)', 'ku', None, None, None)
|
|
120
|
-
Latin = Language('Latin', 'la', None, None, None)
|
|
121
|
-
Latvian = Language('Latvian', 'lv', None, None, None)
|
|
122
|
-
Lithuanian = Language('Lithuanian', 'lt', None, None, None)
|
|
123
|
-
Macedonian = Language('Macedonian', 'mk', None, None, None)
|
|
124
|
-
Malay = Language('Malay', 'ms', None, None, None)
|
|
125
|
-
Malayalam = Language('Malayalam', 'ml', None, None, None)
|
|
126
|
-
Maltese = Language('Maltese', 'mt', None, None, None)
|
|
127
|
-
Maori = Language('Maori', 'mi', None, None, None)
|
|
128
|
-
Marathi = Language('Marathi', 'mr', None, None, None)
|
|
129
|
-
Mongolian = Language('Mongolian', 'mn', None, None, None)
|
|
130
|
-
Myanmar_Burmese = Language('Myanmar (Burmese)', 'my', None, None, None)
|
|
131
|
-
Persian = Language('Persian', 'fa', None, None, None)
|
|
132
|
-
Polish = Language('Polish', 'pl', ['pl'], 'pl-PL', False)
|
|
133
|
-
Portuguese = Language('Portuguese', 'pt', ['pt'], 'pt-PT', False)
|
|
134
|
-
Romanian = Language('Romanian', 'ro', ['ro'], 'ro-RO', True)
|
|
135
|
-
Russian = Language('Russian', 'ru', ['ru'], 'ru-RU', False)
|
|
136
|
-
Scots_Gaelic = Language('Scots Gaelic', 'gd', None, None, None)
|
|
137
|
-
Serbian = Language('Serbian', 'sr', None, None, None)
|
|
138
|
-
Slovak = Language('Slovak', 'sk', None, None, None)
|
|
139
|
-
Slovenian = Language('Slovenian', 'sl', None, None, None)
|
|
140
|
-
Spanish = Language('Spanish', 'es', None, None, None)
|
|
141
|
-
Swedish = Language('Swedish', 'sv', ['sv'], 'sv-SE', True)
|
|
142
|
-
Telugu = Language('Telugu', 'te', None, None, None)
|
|
143
|
-
Thai = Language('Thai', 'th', None, None, None)
|
|
144
|
-
Turkish = Language('Turkish', 'tr', ['tr'], 'tr-TR', True)
|
|
145
|
-
Ukrainian = Language('Ukrainian', 'uk', None, None, None)
|
|
146
|
-
Uzbek = Language('Uzbek', 'uz', None, None, None)
|
|
147
|
-
Vietnamese = Language('Vietnamese', 'vi', None, None, None)
|
|
148
|
-
Welsh = Language('Welsh', 'cy', ['cy'], 'cy-GB', True)
|
|
149
|
-
Zulu = Language('Zulu', 'zu', None, None, None)
|
|
150
|
-
Hebrew = Language('Hebrew', 'he', None, None, None)
|
|
151
|
-
Chinese_Simplified = Language('Chinese (Simplified)', 'zh-cn', ['zh-cn', 'zh-tw'], 'cmn-CN', True)
|
|
152
|
-
Mandarin = Chinese_Simplified
|
|
153
|
-
|
|
154
|
-
nb_NO = Language(None, None, ['nb', 'nn'], 'nb-NO', True)
|
|
155
|
-
pt_BR = Language(None, None, ['pt-br'], 'pt-BR', False)
|
|
156
|
-
Brazilian = pt_BR
|
|
157
|
-
es_ES = Language(None, None, ['es'], 'es-ES', False)
|
|
158
|
-
es_US = Language(None, None, ['es-419'], 'es-US', False)
|
|
159
|
-
|
|
160
|
-
@classmethod
|
|
161
|
-
def find(cls, value, by: str = "name", apply_func: Optional[Callable] = None) -> Language:
|
|
162
|
-
return super().find(value, by, apply_func)
|
|
163
|
-
|
|
164
|
-
@classmethod
|
|
165
|
-
def all_of(cls, attr_name: str = "name", apply_func: Optional[Callable] = None) -> list:
|
|
166
|
-
return super().all_of(attr_name, apply_func)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
@dataclass
|
|
170
|
-
class TTSVoice:
|
|
171
|
-
name: str
|
|
172
|
-
gender: str
|
|
173
|
-
playback_rate: float | int = 1
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
class TTSVoices(_EnumWrapper):
|
|
177
|
-
alto = TTSVoice("alto", "female")
|
|
178
|
-
# female is functionally equal to alto
|
|
179
|
-
female = TTSVoice("female", "female")
|
|
180
|
-
|
|
181
|
-
tenor = TTSVoice("tenor", "male")
|
|
182
|
-
# male is functionally equal to tenor
|
|
183
|
-
male = TTSVoice("male", "male")
|
|
184
|
-
|
|
185
|
-
squeak = TTSVoice("squeak", "female", 1.19)
|
|
186
|
-
giant = TTSVoice("giant", "male", .84)
|
|
187
|
-
kitten = TTSVoice("kitten", "female", 1.41)
|
|
188
|
-
|
|
189
|
-
@classmethod
|
|
190
|
-
def find(cls, value, by: str = "name", apply_func: Optional[Callable] = None) -> TTSVoice:
|
|
191
|
-
return super().find(value, by, apply_func)
|
|
192
|
-
|
|
193
|
-
@classmethod
|
|
194
|
-
def all_of(cls, attr_name: str = "name", apply_func: Optional[Callable] = None) -> Iterable:
|
|
195
|
-
return super().all_of(attr_name, apply_func)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
@dataclass
|
|
199
|
-
class AlertType:
|
|
200
|
-
id: int
|
|
201
|
-
message: str
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
class AlertTypes(_EnumWrapper):
|
|
205
|
-
"""
|
|
206
|
-
Enum for associating alert type indecies with their messages, for use with the str.format() method.
|
|
207
|
-
"""
|
|
208
|
-
# Reference: https://github.com/TimMcCool/scratchattach/issues/304#issuecomment-2800110811
|
|
209
|
-
# NOTE: THE TEXT WITHIN THE BRACES HERE MATTERS! IF YOU WANT TO CHANGE IT, MAKE SURE TO EDIT `site.alert.EducatorAlert`!
|
|
210
|
-
ban = AlertType(0, "{username} was banned.")
|
|
211
|
-
unban = AlertType(1, "{username} was unbanned.")
|
|
212
|
-
excluded_from_homepage = AlertType(2, "{username} was excluded from homepage")
|
|
213
|
-
excluded_from_homepage2 = AlertType(3, "{username} was excluded from homepage") # for some reason there are duplicates
|
|
214
|
-
notified = AlertType(4, "{username} was notified by a Scratch Administrator. Notification type: {notification_type}") # not sure what notification type is
|
|
215
|
-
autoban = AlertType(5, "{username} was banned automatically")
|
|
216
|
-
autoremoved = AlertType(6, "{project} by {username} was removed automatically")
|
|
217
|
-
project_censored2 = AlertType(7, "{project} by {username} was censored.") # <empty #7>
|
|
218
|
-
project_censored = AlertType(20, "{project} by {username} was censored.")
|
|
219
|
-
project_uncensored = AlertType(8, "{project} by {username} was uncensored.")
|
|
220
|
-
project_reviewed2 = AlertType(9, "{project} by {username} was reviewed by a Scratch Administrator.") # <empty #9>
|
|
221
|
-
project_reviewed = AlertType(10, "{project} by {username} was reviewed by a Scratch Administrator.")
|
|
222
|
-
project_deleted = AlertType(11, "{project} by {username} was deleted by a Scratch Administrator.")
|
|
223
|
-
user_deleted2 = AlertType(12, "{username} was deleted by a Scratch Administrator") # <empty #12>
|
|
224
|
-
user_deleted = AlertType(17, "{username} was deleted by a Scratch Administrator")
|
|
225
|
-
studio_reviewed2 = AlertType(13, "{studio} was reviewed by a Scratch Administrator.") # <empty #13>
|
|
226
|
-
studio_reviewed = AlertType(14, "{studio} was reviewed by a Scratch Administrator.")
|
|
227
|
-
studio_deleted = AlertType(15, "{studio} was deleted by a Scratch Administrator.")
|
|
228
|
-
email_confirm2 = AlertType(16, "The email address of {username} was confirmed by a Scratch Administrator") # <empty #16>
|
|
229
|
-
email_confirm = AlertType(18, "The email address of {username} was confirmed by a Scratch Administrator") # no '.' in HTML
|
|
230
|
-
email_unconfirm = AlertType(19, "The email address of {username} was set as not confirmed by a Scratch Administrator")
|
|
231
|
-
automute = AlertType(22, "{username} was automatically muted by our comment filters. The comment they tried to post was: {comment}")
|
|
232
|
-
default = AlertType(-1, "{username} had an admin action performed.") # default case
|
|
233
|
-
|
|
234
|
-
@classmethod
|
|
235
|
-
def find(cls, value, by: str = "id", apply_func: Optional[Callable] = None) -> AlertType:
|
|
236
|
-
return super().find(value, by, apply_func)
|