scratchattach 2.1.6__tar.gz → 2.1.8__tar.gz

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 (45) hide show
  1. {scratchattach-2.1.6 → scratchattach-2.1.8}/PKG-INFO +1 -1
  2. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/__init__.py +1 -0
  3. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/cloud_server.py +2 -2
  4. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/other/other_apis.py +98 -3
  5. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/session.py +2 -1
  6. scratchattach-2.1.8/scratchattach/utils/enums.py +190 -0
  7. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/utils/exceptions.py +28 -5
  8. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/utils/requests.py +3 -2
  9. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach.egg-info/PKG-INFO +1 -1
  10. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach.egg-info/SOURCES.txt +1 -0
  11. {scratchattach-2.1.6 → scratchattach-2.1.8}/setup.py +1 -1
  12. {scratchattach-2.1.6 → scratchattach-2.1.8}/LICENSE +0 -0
  13. {scratchattach-2.1.6 → scratchattach-2.1.8}/README.md +0 -0
  14. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/cloud/__init__.py +0 -0
  15. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/cloud/_base.py +0 -0
  16. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/cloud/cloud.py +0 -0
  17. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/__init__.py +0 -0
  18. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/_base.py +0 -0
  19. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/cloud_events.py +0 -0
  20. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/cloud_recorder.py +0 -0
  21. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/cloud_requests.py +0 -0
  22. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/cloud_storage.py +0 -0
  23. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/combine.py +0 -0
  24. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/filterbot.py +0 -0
  25. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/eventhandlers/message_events.py +0 -0
  26. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/other/__init__.py +0 -0
  27. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/other/project_json_capabilities.py +0 -0
  28. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/__init__.py +0 -0
  29. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/_base.py +0 -0
  30. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/activity.py +0 -0
  31. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/backpack_asset.py +0 -0
  32. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/classroom.py +0 -0
  33. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/cloud_activity.py +0 -0
  34. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/comment.py +0 -0
  35. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/forum.py +0 -0
  36. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/project.py +0 -0
  37. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/studio.py +0 -0
  38. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/site/user.py +0 -0
  39. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/utils/__init__.py +0 -0
  40. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/utils/commons.py +0 -0
  41. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach/utils/encoder.py +0 -0
  42. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach.egg-info/dependency_links.txt +0 -0
  43. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach.egg-info/requires.txt +0 -0
  44. {scratchattach-2.1.6 → scratchattach-2.1.8}/scratchattach.egg-info/top_level.txt +0 -0
  45. {scratchattach-2.1.6 → scratchattach-2.1.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scratchattach
3
- Version: 2.1.6
3
+ Version: 2.1.8
4
4
  Summary: A Scratch API Wrapper
5
5
  Home-page: https://scratchattach.tim1de.net
6
6
  Author: TimMcCool
@@ -10,6 +10,7 @@ from .eventhandlers.combine import MultiEventHandler
10
10
  from .other.other_apis import *
11
11
  from .other.project_json_capabilities import ProjectBody, get_empty_project_pb, get_pb_from_dict, read_sb3_file, download_asset
12
12
  from .utils.encoder import Encoding
13
+ from .utils.enums import Languages, TTSVoices
13
14
 
14
15
  from .site.activity import Activity
15
16
  from .site.backpack_asset import BackpackAsset
@@ -223,7 +223,7 @@ def init_cloud_server(hostname='127.0.0.1', port=8080, *, thread=True, length_li
223
223
  return False
224
224
  return True
225
225
 
226
- def _update(self):
226
+ def _updater(self):
227
227
  try:
228
228
  # Function called when .start() is executed (.start is inherited from BaseEventHandler)
229
229
  print(f"Serving websocket server: ws://{hostname}:{port}")
@@ -241,4 +241,4 @@ def init_cloud_server(hostname='127.0.0.1', port=8080, *, thread=True, length_li
241
241
  self.running = False
242
242
  self.close()
243
243
 
244
- return TwCloudServer(hostname, port=port, websocketclass=TwCloudSocket)
244
+ return TwCloudServer(hostname, port=port, websocketclass=TwCloudSocket)
@@ -1,38 +1,51 @@
1
1
  """Other Scratch API-related functions"""
2
2
 
3
+ import json
4
+
3
5
  from ..utils import commons
6
+ from ..utils.exceptions import BadRequest, InvalidLanguage, InvalidTTSGender
4
7
  from ..utils.requests import Requests as requests
5
- import json
8
+ from ..utils.enums import Languages, Language, TTSVoices, TTSVoice
9
+
6
10
 
7
11
  # --- Front page ---
8
12
 
9
13
  def get_news(*, limit=10, offset=0):
10
- return commons.api_iterative("https://api.scratch.mit.edu/news", limit = limit, offset = offset)
14
+ return commons.api_iterative("https://api.scratch.mit.edu/news", limit=limit, offset=offset)
15
+
11
16
 
12
17
  def featured_data():
13
18
  return requests.get("https://api.scratch.mit.edu/proxy/featured").json()
14
19
 
20
+
15
21
  def featured_projects():
16
22
  return featured_data()["community_featured_projects"]
17
23
 
24
+
18
25
  def featured_studios():
19
26
  return featured_data()["community_featured_studios"]
20
27
 
28
+
21
29
  def top_loved():
22
30
  return featured_data()["community_most_loved_projects"]
23
31
 
32
+
24
33
  def top_remixed():
25
34
  return featured_data()["community_most_remixed_projects"]
26
35
 
36
+
27
37
  def newest_projects():
28
38
  return featured_data()["community_newest_projects"]
29
39
 
40
+
30
41
  def curated_projects():
31
42
  return featured_data()["curator_top_projects"]
32
43
 
44
+
33
45
  def design_studio_projects():
34
46
  return featured_data()["scratch_design_studio"]
35
47
 
48
+
36
49
  # --- Statistics ---
37
50
 
38
51
  def total_site_stats():
@@ -40,14 +53,17 @@ def total_site_stats():
40
53
  data.pop("_TS")
41
54
  return data
42
55
 
56
+
43
57
  def monthly_site_traffic():
44
58
  data = requests.get("https://scratch.mit.edu/statistics/data/monthly-ga/").json()
45
59
  data.pop("_TS")
46
60
  return data
47
61
 
62
+
48
63
  def country_counts():
49
64
  return requests.get("https://scratch.mit.edu/statistics/data/monthly/").json()["country_distribution"]
50
65
 
66
+
51
67
  def age_distribution():
52
68
  data = requests.get("https://scratch.mit.edu/statistics/data/monthly/").json()["age_distribution_data"][0]["values"]
53
69
  return_data = {}
@@ -55,18 +71,23 @@ def age_distribution():
55
71
  return_data[value["x"]] = value["y"]
56
72
  return return_data
57
73
 
74
+
58
75
  def monthly_comment_activity():
59
76
  return requests.get("https://scratch.mit.edu/statistics/data/monthly/").json()["comment_data"]
60
77
 
78
+
61
79
  def monthly_project_shares():
62
80
  return requests.get("https://scratch.mit.edu/statistics/data/monthly/").json()["project_data"]
63
81
 
82
+
64
83
  def monthly_active_users():
65
84
  return requests.get("https://scratch.mit.edu/statistics/data/monthly/").json()["active_user_data"]
66
85
 
86
+
67
87
  def monthly_activity_trends():
68
88
  return requests.get("https://scratch.mit.edu/statistics/data/monthly/").json()["activity_data"]
69
89
 
90
+
70
91
  # --- CSRF Token Generation API ---
71
92
 
72
93
  def get_csrf_token():
@@ -80,32 +101,41 @@ def get_csrf_token():
80
101
  "https://scratch.mit.edu/csrf_token/"
81
102
  ).headers["set-cookie"].split(";")[3][len(" Path=/, scratchcsrftoken="):]
82
103
 
104
+
83
105
  # --- Various other api.scratch.mit.edu API endpoints ---
84
106
 
85
107
  def get_health():
86
108
  return requests.get("https://api.scratch.mit.edu/health").json()
87
109
 
110
+
88
111
  def get_total_project_count() -> int:
89
112
  return requests.get("https://api.scratch.mit.edu/projects/count/all").json()["count"]
90
113
 
114
+
91
115
  def check_username(username):
92
116
  return requests.get(f"https://api.scratch.mit.edu/accounts/checkusername/{username}").json()["msg"]
93
117
 
118
+
94
119
  def check_password(password):
95
- return requests.post("https://api.scratch.mit.edu/accounts/checkpassword/", json={"password":password}).json()["msg"]
120
+ return requests.post("https://api.scratch.mit.edu/accounts/checkpassword/", json={"password": password}).json()[
121
+ "msg"]
122
+
96
123
 
97
124
  # --- April fools endpoints ---
98
125
 
99
126
  def aprilfools_get_counter() -> int:
100
127
  return requests.get("https://api.scratch.mit.edu/surprise").json()["surprise"]
101
128
 
129
+
102
130
  def aprilfools_increment_counter() -> int:
103
131
  return requests.post("https://api.scratch.mit.edu/surprise").json()["surprise"]
104
132
 
133
+
105
134
  # --- Resources ---
106
135
  def get_resource_urls():
107
136
  return requests.get("https://resources.scratch.mit.edu/localized-urls.json").json()
108
137
 
138
+
109
139
  # --- Misc ---
110
140
  # I'm not sure what to label this as
111
141
  def scratch_team_members() -> dict:
@@ -115,3 +145,68 @@ def scratch_team_members() -> dict:
115
145
  text = text.split("\"}]')")[0] + "\"}]"
116
146
 
117
147
  return json.loads(text)
148
+
149
+
150
+ def translate(language: str | Languages, text: str = "hello"):
151
+ if isinstance(language, str):
152
+ lang = Languages.find_by_attrs(language.lower(), ["code", "tts_locale", "name"], str.lower)
153
+ elif isinstance(language, Languages):
154
+ lang = language.value
155
+ else:
156
+ lang = language
157
+
158
+ if not isinstance(lang, Language):
159
+ raise InvalidLanguage(f"{language} is not a language")
160
+
161
+ if lang.code is None:
162
+ raise InvalidLanguage(f"{lang} is not a valid translate language")
163
+
164
+ response_json = requests.get(
165
+ f"https://translate-service.scratch.mit.edu/translate?language={lang.code}&text={text}").json()
166
+
167
+ if "result" in response_json:
168
+ return response_json["result"]
169
+ else:
170
+ raise BadRequest(f"Language '{language}' does not seem to be valid.\nResponse: {response_json}")
171
+
172
+
173
+ def text2speech(text: str = "hello", voice_name: str = "female", language: str = "en-US"):
174
+ """
175
+ Sends a request to Scratch's TTS synthesis service.
176
+ Returns:
177
+ - The TTS audio (mp3) as bytes
178
+ - The playback rate (e.g. for giant it would be 0.84)
179
+ """
180
+ if isinstance(voice_name, str):
181
+ voice = TTSVoices.find_by_attrs(voice_name.lower(), ["name", "gender"], str.lower)
182
+ elif isinstance(voice_name, TTSVoices):
183
+ voice = voice_name.value
184
+ else:
185
+ voice = voice_name
186
+
187
+ if not isinstance(voice, TTSVoice):
188
+ raise InvalidTTSGender(f"TTS Gender {voice_name} is not supported.")
189
+
190
+ # If it's kitten, make sure to change everything to just meows
191
+ if voice.name == "kitten":
192
+ text = ''
193
+ for word in text.split(' '):
194
+ if word.strip() != '':
195
+ text += "meow "
196
+
197
+ if isinstance(language, str):
198
+ lang = Languages.find_by_attrs(language.lower(), ["code", "tts_locale", "name"], str.lower)
199
+ elif isinstance(language, Languages):
200
+ lang = language.value
201
+ else:
202
+ lang = language
203
+
204
+ if not isinstance(lang, Language):
205
+ raise InvalidLanguage(f"Language '{language}' is not a language")
206
+
207
+ if lang.tts_locale is None:
208
+ raise InvalidLanguage(f"Language '{language}' is not a valid TTS language")
209
+
210
+ response = requests.get(f"https://synthesis-service.scratch.mit.edu/synth"
211
+ f"?locale={lang.tts_locale}&gender={voice.gender}&text={text}")
212
+ return response.content, voice.playback_rate
@@ -54,7 +54,7 @@ class Session(BaseSiteComponent):
54
54
  '''
55
55
 
56
56
  def __str__(self):
57
- return "Login for account: {self.username}"
57
+ return f"Login for account: {self.username}"
58
58
 
59
59
  def __init__(self, **entries):
60
60
 
@@ -839,6 +839,7 @@ def login(username, password, *, timeout=10) -> Session:
839
839
  request = requests.post(
840
840
  "https://scratch.mit.edu/login/", data=data, headers=_headers,
841
841
  timeout = timeout,
842
+ errorhandling = False
842
843
  )
843
844
  try:
844
845
  session_id = str(re.search('"(.*)"', request.headers["Set-Cookie"]).group())
@@ -0,0 +1,190 @@
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
+
6
+ from enum import Enum
7
+ from dataclasses import dataclass
8
+
9
+ from typing import Callable, Iterable
10
+
11
+
12
+ @dataclass(init=True, repr=True)
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: 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 apply_func(getattr(item_obj, by)) == value:
40
+ return item_obj
41
+ except TypeError:
42
+ pass
43
+
44
+ @classmethod
45
+ def all_of(cls, attr_name: str, apply_func: Callable = None) -> Iterable:
46
+ """
47
+ Returns the list of each listed enum item's specified attribute by "attr_name"
48
+
49
+ i.e. Languages.all_of("name") will return a list of names:
50
+ ["Albanian", "Amharic", ...]
51
+
52
+ The apply_func function will be applied to every list item,
53
+ i.e. Languages.all_of("name", str.lower) will return the same except in lowercase:
54
+ ["albanian", "amharic", ...]
55
+ """
56
+ if apply_func is None:
57
+ def apply_func(x):
58
+ return x
59
+
60
+ for item in cls:
61
+ item_obj = item.value
62
+ attr = getattr(item_obj, attr_name)
63
+ try:
64
+ yield apply_func(attr)
65
+
66
+ except TypeError:
67
+ yield attr
68
+
69
+ @classmethod
70
+ def find_by_attrs(cls, value, bys: list[str], apply_func: Callable = None) -> list:
71
+ """
72
+ Calls the EnumWrapper.by function multiple times until a match is found, using the provided 'by' attribute names
73
+ """
74
+ for by in bys:
75
+ ret = cls.find(value, by, apply_func)
76
+ if ret is not None:
77
+ return ret
78
+
79
+
80
+ class Languages(_EnumWrapper):
81
+ Albanian = Language('Albanian', 'sq', None, None, None)
82
+ Amharic = Language('Amharic', 'am', None, None, None)
83
+ Arabic = Language('Arabic', 'ar', ['ar'], 'arb', True)
84
+ Armenian = Language('Armenian', 'hy', None, None, None)
85
+ Azerbaijani = Language('Azerbaijani', 'az', None, None, None)
86
+ Basque = Language('Basque', 'eu', None, None, None)
87
+ Belarusian = Language('Belarusian', 'be', None, None, None)
88
+ Bulgarian = Language('Bulgarian', 'bg', None, None, None)
89
+ Catalan = Language('Catalan', 'ca', None, None, None)
90
+ Chinese_Traditional = Language('Chinese (Traditional)', 'zh-tw', ['zh-cn', 'zh-tw'], 'cmn-CN', True)
91
+ Croatian = Language('Croatian', 'hr', None, None, None)
92
+ Czech = Language('Czech', 'cs', None, None, None)
93
+ Danish = Language('Danish', 'da', ['da'], 'da-DK', False)
94
+ Dutch = Language('Dutch', 'nl', ['nl'], 'nl-NL', False)
95
+ English = Language('English', 'en', ['en'], 'en-US', False)
96
+ Esperanto = Language('Esperanto', 'eo', None, None, None)
97
+ Estonian = Language('Estonian', 'et', None, None, None)
98
+ Finnish = Language('Finnish', 'fi', None, None, None)
99
+ French = Language('French', 'fr', ['fr'], 'fr-FR', False)
100
+ Galician = Language('Galician', 'gl', None, None, None)
101
+ German = Language('German', 'de', ['de'], 'de-DE', False)
102
+ Greek = Language('Greek', 'el', None, None, None)
103
+ Haitian_Creole = Language('Haitian Creole', 'ht', None, None, None)
104
+ Hindi = Language('Hindi', 'hi', ['hi'], 'hi-IN', True)
105
+ Hungarian = Language('Hungarian', 'hu', None, None, None)
106
+ Icelandic = Language('Icelandic', 'is', ['is'], 'is-IS', False)
107
+ Indonesian = Language('Indonesian', 'id', None, None, None)
108
+ Irish = Language('Irish', 'ga', None, None, None)
109
+ Italian = Language('Italian', 'it', ['it'], 'it-IT', False)
110
+ Japanese = Language('Japanese', 'ja', ['ja', 'ja-hira'], 'ja-JP', False)
111
+ Kannada = Language('Kannada', 'kn', None, None, None)
112
+ Korean = Language('Korean', 'ko', ['ko'], 'ko-KR', True)
113
+ Kurdish_Kurmanji = Language('Kurdish (Kurmanji)', 'ku', None, None, None)
114
+ Latin = Language('Latin', 'la', None, None, None)
115
+ Latvian = Language('Latvian', 'lv', None, None, None)
116
+ Lithuanian = Language('Lithuanian', 'lt', None, None, None)
117
+ Macedonian = Language('Macedonian', 'mk', None, None, None)
118
+ Malay = Language('Malay', 'ms', None, None, None)
119
+ Malayalam = Language('Malayalam', 'ml', None, None, None)
120
+ Maltese = Language('Maltese', 'mt', None, None, None)
121
+ Maori = Language('Maori', 'mi', None, None, None)
122
+ Marathi = Language('Marathi', 'mr', None, None, None)
123
+ Mongolian = Language('Mongolian', 'mn', None, None, None)
124
+ Myanmar_Burmese = Language('Myanmar (Burmese)', 'my', None, None, None)
125
+ Persian = Language('Persian', 'fa', None, None, None)
126
+ Polish = Language('Polish', 'pl', ['pl'], 'pl-PL', False)
127
+ Portuguese = Language('Portuguese', 'pt', ['pt'], 'pt-PT', False)
128
+ Romanian = Language('Romanian', 'ro', ['ro'], 'ro-RO', True)
129
+ Russian = Language('Russian', 'ru', ['ru'], 'ru-RU', False)
130
+ Scots_Gaelic = Language('Scots Gaelic', 'gd', None, None, None)
131
+ Serbian = Language('Serbian', 'sr', None, None, None)
132
+ Slovak = Language('Slovak', 'sk', None, None, None)
133
+ Slovenian = Language('Slovenian', 'sl', None, None, None)
134
+ Spanish = Language('Spanish', 'es', None, None, None)
135
+ Swedish = Language('Swedish', 'sv', ['sv'], 'sv-SE', True)
136
+ Telugu = Language('Telugu', 'te', None, None, None)
137
+ Thai = Language('Thai', 'th', None, None, None)
138
+ Turkish = Language('Turkish', 'tr', ['tr'], 'tr-TR', True)
139
+ Ukrainian = Language('Ukrainian', 'uk', None, None, None)
140
+ Uzbek = Language('Uzbek', 'uz', None, None, None)
141
+ Vietnamese = Language('Vietnamese', 'vi', None, None, None)
142
+ Welsh = Language('Welsh', 'cy', ['cy'], 'cy-GB', True)
143
+ Zulu = Language('Zulu', 'zu', None, None, None)
144
+ Hebrew = Language('Hebrew', 'he', None, None, None)
145
+ Chinese_Simplified = Language('Chinese (Simplified)', 'zh-cn', ['zh-cn', 'zh-tw'], 'cmn-CN', True)
146
+ Mandarin = Chinese_Simplified
147
+
148
+ nb_NO = Language(None, None, ['nb', 'nn'], 'nb-NO', True)
149
+ pt_BR = Language(None, None, ['pt-br'], 'pt-BR', False)
150
+ Brazilian = pt_BR
151
+ es_ES = Language(None, None, ['es'], 'es-ES', False)
152
+ es_US = Language(None, None, ['es-419'], 'es-US', False)
153
+
154
+ @classmethod
155
+ def find(cls, value, by: str = "name", apply_func: Callable = None) -> Language:
156
+ return super().find(value, by, apply_func)
157
+
158
+ @classmethod
159
+ def all_of(cls, attr_name: str = "name", apply_func: Callable = None) -> list:
160
+ return super().all_of(attr_name, apply_func)
161
+
162
+
163
+ @dataclass(init=True, repr=True)
164
+ class TTSVoice:
165
+ name: str
166
+ gender: str
167
+ playback_rate: float | int = 1
168
+
169
+
170
+ class TTSVoices(_EnumWrapper):
171
+ alto = TTSVoice("alto", "female")
172
+ # female is functionally equal to alto
173
+ female = TTSVoice("female", "female")
174
+
175
+ tenor = TTSVoice("tenor", "male")
176
+ # male is functionally equal to tenor
177
+ male = TTSVoice("male", "male")
178
+
179
+ squeak = TTSVoice("squeak", "female", 1.19)
180
+ giant = TTSVoice("giant", "male", .84)
181
+ kitten = TTSVoice("kitten", "female", 1.41)
182
+
183
+ @classmethod
184
+ def find(cls, value, by: str = "name", apply_func: Callable = None) -> TTSVoice:
185
+ return super().find(value, by, apply_func)
186
+
187
+ @classmethod
188
+ def all_of(cls, attr_name: str = "name", apply_func: Callable = None) -> Iterable:
189
+ return super().all_of(attr_name, apply_func)
190
+
@@ -18,7 +18,6 @@ class Unauthenticated(Exception):
18
18
  def __init__(self, message=""):
19
19
  self.message = "No login / session connected.\n\nThe object on which the method was called was created using scratchattach.get_xyz()\nUse session.connect_xyz() instead (xyz is a placeholder for user / project / cloud / ...).\n\nMore information: https://scratchattach.readthedocs.io/en/latest/scratchattach.html#scratchattach.utils.exceptions.Unauthenticated"
20
20
  super().__init__(self.message)
21
- pass
22
21
 
23
22
 
24
23
  class Unauthorized(Exception):
@@ -32,7 +31,6 @@ class Unauthorized(Exception):
32
31
  self.message = "The user corresponding to the connected login / session is not allowed to perform this action."
33
32
  super().__init__(self.message)
34
33
 
35
- pass
36
34
 
37
35
  class XTokenError(Exception):
38
36
  """
@@ -43,6 +41,7 @@ class XTokenError(Exception):
43
41
 
44
42
  pass
45
43
 
44
+
46
45
  # Not found errors:
47
46
 
48
47
  class UserNotFound(Exception):
@@ -60,6 +59,7 @@ class ProjectNotFound(Exception):
60
59
 
61
60
  pass
62
61
 
62
+
63
63
  class ClassroomNotFound(Exception):
64
64
  """
65
65
  Raised when a non-existent Classroom is requested.
@@ -75,15 +75,32 @@ class StudioNotFound(Exception):
75
75
 
76
76
  pass
77
77
 
78
+
78
79
  class ForumContentNotFound(Exception):
79
80
  """
80
81
  Raised when a non-existent forum topic / post is requested.
81
82
  """
82
83
  pass
83
84
 
85
+
84
86
  class CommentNotFound(Exception):
85
87
  pass
86
88
 
89
+
90
+ # Invalid inputs
91
+ class InvalidLanguage(Exception):
92
+ """
93
+ Raised when an invalid language/language code/language object is provided, for TTS or Translate
94
+ """
95
+ pass
96
+
97
+
98
+ class InvalidTTSGender(Exception):
99
+ """
100
+ Raised when an invalid TTS gender is provided.
101
+ """
102
+ pass
103
+
87
104
  # API errors:
88
105
 
89
106
  class LoginFailure(Exception):
@@ -95,6 +112,7 @@ class LoginFailure(Exception):
95
112
 
96
113
  pass
97
114
 
115
+
98
116
  class FetchError(Exception):
99
117
  """
100
118
  Raised when getting information from the Scratch API fails. This can have various reasons. Make sure all provided arguments are valid.
@@ -102,6 +120,7 @@ class FetchError(Exception):
102
120
 
103
121
  pass
104
122
 
123
+
105
124
  class BadRequest(Exception):
106
125
  """
107
126
  Raised when the Scratch API responds with a "Bad Request" error message. This can have various reasons. Make sure all provided arguments are valid.
@@ -117,6 +136,7 @@ class Response429(Exception):
117
136
 
118
137
  pass
119
138
 
139
+
120
140
  class CommentPostFailure(Exception):
121
141
  """
122
142
  Raised when a comment fails to post. This can have various reasons.
@@ -124,12 +144,14 @@ class CommentPostFailure(Exception):
124
144
 
125
145
  pass
126
146
 
147
+
127
148
  class APIError(Exception):
128
149
  """
129
150
  For API errors that can't be classified into one of the above errors
130
151
  """
131
152
  pass
132
153
 
154
+
133
155
  class ScrapeError(Exception):
134
156
  """
135
157
  Raised when something goes wrong while web-scraping a page with bs4.
@@ -137,9 +159,10 @@ class ScrapeError(Exception):
137
159
 
138
160
  pass
139
161
 
162
+
140
163
  # Cloud / encoding errors:
141
164
 
142
- class ConnectionError(Exception):
165
+ class CloudConnectionError(Exception):
143
166
  """
144
167
  Raised when connecting to Scratch's cloud server fails. This can have various reasons.
145
168
  """
@@ -172,12 +195,12 @@ class RequestNotFound(Exception):
172
195
 
173
196
  pass
174
197
 
198
+
175
199
  # Websocket server errors:
176
200
 
177
201
  class WebsocketServerError(Exception):
178
-
179
202
  """
180
203
  Raised when the self-hosted cloud websocket server fails to start.
181
204
  """
182
205
 
183
- pass
206
+ pass
@@ -31,12 +31,13 @@ class Requests:
31
31
  return r
32
32
 
33
33
  @staticmethod
34
- def post(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None):
34
+ def post(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None, errorhandling=True):
35
35
  try:
36
36
  r = requests.post(url, data=data, json=json, headers=headers, cookies=cookies, params=params, timeout=timeout, proxies=proxies)
37
37
  except Exception as e:
38
38
  raise exceptions.FetchError(e)
39
- Requests.check_response(r)
39
+ if errorhandling:
40
+ Requests.check_response(r)
40
41
  return r
41
42
 
42
43
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scratchattach
3
- Version: 2.1.6
3
+ Version: 2.1.8
4
4
  Summary: A Scratch API Wrapper
5
5
  Home-page: https://scratchattach.tim1de.net
6
6
  Author: TimMcCool
@@ -38,5 +38,6 @@ scratchattach/site/user.py
38
38
  scratchattach/utils/__init__.py
39
39
  scratchattach/utils/commons.py
40
40
  scratchattach/utils/encoder.py
41
+ scratchattach/utils/enums.py
41
42
  scratchattach/utils/exceptions.py
42
43
  scratchattach/utils/requests.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
  import codecs
3
3
  import os
4
4
 
5
- VERSION = '2.1.6'
5
+ VERSION = '2.1.8'
6
6
  DESCRIPTION = 'A Scratch API Wrapper'
7
7
  LONG_DESCRIPTION = DESCRIPTION
8
8
 
File without changes
File without changes
File without changes