@@ -85,16 +85,28 @@ def __init__(self, **entries):
8585 }
8686
8787 def _update_from_dict (self , data ):
88+ # Note: there are a lot more things you can get from this data dict.
89+ # Maybe it would be a good idea to also store the dict itself?
90+ # self.data = data
91+
8892 self .xtoken = data ['user' ]['token' ]
8993 self ._headers ["X-Token" ] = self .xtoken
94+
95+ self .has_outstanding_email_confirmation = data ["flags" ]["has_outstanding_email_confirmation" ]
96+
9097 self .email = data ["user" ]["email" ]
98+
9199 self .new_scratcher = data ["permissions" ]["new_scratcher" ]
92100 self .mute_status = data ["permissions" ]["mute_status" ]
101+
93102 self .username = data ["user" ]["username" ]
94103 self ._username = data ["user" ]["username" ]
95104 self .banned = data ["user" ]["banned" ]
105+
96106 if self .banned :
97107 warnings .warn (f"Warning: The account { self ._username } you logged in to is BANNED. Some features may not work properly." )
108+ if self .has_outstanding_email_confirmation :
109+ warnings .warn (f"Warning: The account { self ._username } you logged is not email confirmed. Some features may not work properly." )
98110 return True
99111
100112 def connect_linked_user (self ) -> 'user.User' :
@@ -115,6 +127,90 @@ def get_linked_user(self):
115127 # backwards compatibility with v1
116128 return self .connect_linked_user () # To avoid inconsistencies with "connect" and "get", this function was renamed
117129
130+ def set_country (self , country : str = "Antarctica" ):
131+ requests .post ("https://scratch.mit.edu/accounts/settings/" ,
132+ data = {"country" : country },
133+ headers = self ._headers , cookies = self ._cookies )
134+
135+ def change_password (self , old_password : str , new_password : str = None ):
136+ if new_password is None or new_password == old_password :
137+ return
138+ requests .post ("https://scratch.mit.edu/accounts/password_change/" ,
139+ data = {"old_password" : old_password ,
140+ "new_password1" : new_password ,
141+ "new_password2" : new_password },
142+ headers = self ._headers , cookies = self ._cookies )
143+
144+ def resend_email (self , password : str ):
145+ """
146+ Sends a request to resend a confirmation email for this session's account
147+
148+ Keyword arguments:
149+ password (str): Password associated with the session (not stored)
150+ """
151+ requests .post ("https://scratch.mit.edu/accounts/email_change/" ,
152+ data = {"email_address" : self .new_email_address ,
153+ "password" : password },
154+ headers = self ._headers , cookies = self ._cookies )
155+
156+ def change_email (self , new_email : str , password : str ):
157+ """
158+ Sends a request to change the email of this session
159+
160+ Keyword arguments:
161+ new_email (str): The email you want to switch to
162+ password (str): Password associated with the session (not stored)
163+ """
164+ requests .post ("https://scratch.mit.edu/accounts/email_change/" ,
165+ data = {"email_address" : new_email ,
166+ "password" : password },
167+ headers = self ._headers , cookies = self ._cookies )
168+
169+ @property
170+ def new_email_address (self ) -> str | None :
171+ """
172+ Gets the (unconfirmed) email address that this session has requested to transfer to, if any, otherwise the current address.
173+
174+ Returns:
175+ str: The email that this session wants to switch to
176+ """
177+ response = requests .get ("https://scratch.mit.edu/accounts/email_change/" ,
178+ headers = self ._headers , cookies = self ._cookies )
179+
180+ soup = BeautifulSoup (response .content , "html.parser" )
181+
182+ email = None
183+ for label_span in soup .find_all ("span" , {"class" : "label" }):
184+ if label_span .contents [0 ] == "New Email Address" :
185+ return label_span .parent .contents [- 1 ].text .strip ("\n " )
186+ elif label_span .contents [0 ] == "Current Email Address" :
187+ email = label_span .parent .contents [- 1 ].text .strip ("\n " )
188+
189+ return email
190+
191+ def delete_account (self , * , password : str , delete_projects : bool = False ):
192+ """
193+ !!! Dangerous !!!
194+ Sends a request to delete the account that is associated with this session.
195+ You can cancel the deletion simply by logging back in (including using sa.login(username, password))
196+
197+ Keyword arguments:
198+ password (str): The password associated with the account
199+ delete_projects (bool): Whether to delete all the projects as well
200+ """
201+ requests .post ("https://scratch.mit.edu/accounts/settings/delete_account/" ,
202+ data = {
203+ "delete_state" : "delbyusrwproj" if delete_projects else "delbyusr" ,
204+ "password" : password
205+ }, headers = self ._headers , cookies = self ._cookies )
206+
207+ def logout (self ):
208+ """
209+ Sends a logout request to scratch. Might not do anything, might log out this account on other ips/sessions? I am not sure
210+ """
211+ requests .post ("https://scratch.mit.edu/accounts/logout/" ,
212+ headers = self ._headers , cookies = self ._cookies )
213+
118214 def messages (self , * , limit = 40 , offset = 0 , date_limit = None , filter_by = None ):
119215 '''
120216 Returns the messages.
@@ -255,7 +351,7 @@ def connect_pb_from_file(path_to_file) -> 'project_json_capabilities.ProjectBody
255351 pb = project_json_capabilities .ProjectBody ()
256352 pb .from_json (project_json_capabilities ._load_sb3_file (path_to_file ))
257353 return pb
258-
354+
259355 def download_asset (asset_id_with_file_ext , * , filename = None , dir = "" ):
260356 if not (dir .endswith ("/" ) or dir .endswith ("\\ " )):
261357 dir = dir + "/"
@@ -463,7 +559,7 @@ def mystuff_studios(self, filter_arg="all", *, page=1, sort_by="", descending=Tr
463559 except Exception :
464560 raise (exceptions .FetchError )
465561
466-
562+
467563 def backpack (self ,limit = 20 , offset = 0 ):
468564 '''
469565 Lists the assets that are in the backpack of the user associated with the session.
@@ -476,7 +572,7 @@ def backpack(self,limit=20, offset=0):
476572 limit = limit , offset = offset , headers = self ._headers
477573 )
478574 return commons .parse_object_list (data , backpack_asset .BackpackAsset , self )
479-
575+
480576 def delete_from_backpack (self , backpack_asset_id ):
481577 '''
482578 Deletes an asset from the backpack.
@@ -788,7 +884,7 @@ def login(username, password, *, timeout=10) -> Session:
788884 except Exception :
789885 raise exceptions .LoginFailure (
790886 "Either the provided authentication data is wrong or your network is banned from Scratch.\n \n If you're using an online IDE (like replit.com) Scratch possibly banned its IP adress. In this case, try logging in with your session id: https://github.com/TimMcCool/scratchattach/wiki#logging-in" )
791-
887+
792888 # Create session object:
793889 return login_by_id (session_id , username = username , password = password )
794890
0 commit comments