retro-sdk 0.1.0__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.
- retro_sdk-0.1.0/.gitignore +2 -0
- retro_sdk-0.1.0/.python-version +1 -0
- retro_sdk-0.1.0/LICENSE +21 -0
- retro_sdk-0.1.0/PKG-INFO +7 -0
- retro_sdk-0.1.0/README.md +0 -0
- retro_sdk-0.1.0/pyproject.toml +11 -0
- retro_sdk-0.1.0/retro_sdk/__init__.py +3 -0
- retro_sdk-0.1.0/retro_sdk/client.py +257 -0
- retro_sdk-0.1.0/retro_sdk.egg-info/PKG-INFO +8 -0
- retro_sdk-0.1.0/retro_sdk.egg-info/SOURCES.txt +10 -0
- retro_sdk-0.1.0/retro_sdk.egg-info/dependency_links.txt +1 -0
- retro_sdk-0.1.0/retro_sdk.egg-info/requires.txt +1 -0
- retro_sdk-0.1.0/retro_sdk.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.8
|
retro_sdk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sofia Egan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
retro_sdk-0.1.0/PKG-INFO
ADDED
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "retro-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "python sdk for retro, the photo sharing app!"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
dependencies = ["requests"]
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
class Retro:
|
|
4
|
+
def __init__(self, refresh_token=None):
|
|
5
|
+
self.refresh_token = refresh_token
|
|
6
|
+
self.access_token = None
|
|
7
|
+
self.last_checked_time = None
|
|
8
|
+
self.web_api_key = "AIzaSyDVXcY0s4ZeREh43EYzsHqbEWcCJ6Ism5w"
|
|
9
|
+
if refresh_token:
|
|
10
|
+
self._refresh_auth_token()
|
|
11
|
+
|
|
12
|
+
def _refresh_auth_token(self):
|
|
13
|
+
url = f"https://securetoken.googleapis.com/v1/token?key={self.web_api_key}"
|
|
14
|
+
payload = {"grantType": "refresh_token", "refreshToken": self.refresh_token}
|
|
15
|
+
r = requests.post(url, json=payload)
|
|
16
|
+
try:
|
|
17
|
+
r.raise_for_status()
|
|
18
|
+
except requests.HTTPError as e:
|
|
19
|
+
print(f"Error: {r.text}")
|
|
20
|
+
raise
|
|
21
|
+
self.access_token = r.json().get("access_token")
|
|
22
|
+
|
|
23
|
+
def set_last_checked_time(self, time):
|
|
24
|
+
self.last_checked_time = time
|
|
25
|
+
|
|
26
|
+
def get_last_checked_time(self):
|
|
27
|
+
return self.last_checked_time
|
|
28
|
+
|
|
29
|
+
def get_auth_header(self):
|
|
30
|
+
return {"Authorization": f"Firebase {self.access_token}"}
|
|
31
|
+
|
|
32
|
+
def get_refresh_token(self, token, verbose=False):
|
|
33
|
+
url = f"https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key={self.web_api_key}"
|
|
34
|
+
headers = {"content-type": "application/json"}
|
|
35
|
+
payload = {"token": token, "returnSecureToken": True}
|
|
36
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
37
|
+
try:
|
|
38
|
+
r.raise_for_status()
|
|
39
|
+
except requests.HTTPError as e:
|
|
40
|
+
print(f"Error: {r.text}")
|
|
41
|
+
raise
|
|
42
|
+
r_json = r.json()
|
|
43
|
+
self.refresh_token = r_json["refreshToken"]
|
|
44
|
+
print(r_json)
|
|
45
|
+
if not verbose:
|
|
46
|
+
return
|
|
47
|
+
return r_json
|
|
48
|
+
|
|
49
|
+
def send_code(self, phone_number, custom_data=None, verbose=False):
|
|
50
|
+
# phone number must be in international format
|
|
51
|
+
# custom payload format
|
|
52
|
+
# {
|
|
53
|
+
# "data": {
|
|
54
|
+
# "deviceId": device_id,
|
|
55
|
+
# "appVersion": app_version,
|
|
56
|
+
# "phoneNumber": phone_number,
|
|
57
|
+
# "platform": platform,
|
|
58
|
+
# "osVersion": os_version,
|
|
59
|
+
# "dispatchId": dispatch_id,
|
|
60
|
+
# "deviceModel": device_model
|
|
61
|
+
# }
|
|
62
|
+
# }
|
|
63
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/sendCode"
|
|
64
|
+
headers = {"content-type": "application/json"}
|
|
65
|
+
if custom_data==None:
|
|
66
|
+
payload = {
|
|
67
|
+
"data": {
|
|
68
|
+
"phoneNumber": phone_number
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else:
|
|
72
|
+
payload = custom_data
|
|
73
|
+
|
|
74
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
75
|
+
try:
|
|
76
|
+
r.raise_for_status()
|
|
77
|
+
except requests.HTTPError as e:
|
|
78
|
+
print(f"Error: {r.text}")
|
|
79
|
+
raise
|
|
80
|
+
r_json = r.json()
|
|
81
|
+
self.prev_authenticationUuid = r_json["result"]["authenticationUuid"]
|
|
82
|
+
self.phone_number = phone_number
|
|
83
|
+
if not verbose:
|
|
84
|
+
return r_json["result"]["authenticationUuid"]
|
|
85
|
+
return r_json
|
|
86
|
+
|
|
87
|
+
def verify_code(self, code, authenticationUuid=None, verbose=False):
|
|
88
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/verifyCode"
|
|
89
|
+
headers = {"content-type": "application/json"}
|
|
90
|
+
payload = {"data": {"authenticationUuid": authenticationUuid if authenticationUuid else self.prev_authenticationUuid, "code": code}}
|
|
91
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
92
|
+
try:
|
|
93
|
+
r.raise_for_status()
|
|
94
|
+
except requests.HTTPError as e:
|
|
95
|
+
print(f"Error: {r.text}")
|
|
96
|
+
raise
|
|
97
|
+
r_json = r.json()
|
|
98
|
+
self.access_token = r_json["result"]["token"]
|
|
99
|
+
self.get_refresh_token(self.access_token)
|
|
100
|
+
if not verbose:
|
|
101
|
+
return
|
|
102
|
+
return r_json
|
|
103
|
+
|
|
104
|
+
def download_profile_photo(self, user_id, filename, output_path): # make it return the image in some reasonable format
|
|
105
|
+
url = f"https://firebasestorage.googleapis.com/v0/b/retro-media-multi/o/profilePhotos%2F{user_id}%2F{filename}?alt=media"
|
|
106
|
+
r = requests.get(url, headers=self.get_auth_header())
|
|
107
|
+
try:
|
|
108
|
+
r.raise_for_status()
|
|
109
|
+
except requests.HTTPError as e:
|
|
110
|
+
print(f"Error: {r.text}")
|
|
111
|
+
raise
|
|
112
|
+
with open(output_path, "wb") as f:
|
|
113
|
+
f.write(r.content)
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
def get_media_metadata(self, user_id, week, filename):
|
|
117
|
+
url = f"https://firebasestorage.googleapis.com/v0/b/retro-media-multi/o/media%2F{user_id}%2F{week}%2F{filename}"
|
|
118
|
+
r = requests.get(url, headers=self.get_auth_header())
|
|
119
|
+
try:
|
|
120
|
+
r.raise_for_status()
|
|
121
|
+
except requests.HTTPError as e:
|
|
122
|
+
print(f"Error: {r.text}")
|
|
123
|
+
raise
|
|
124
|
+
return r.json()
|
|
125
|
+
|
|
126
|
+
def list_files_in_folder(self, user_id, week):
|
|
127
|
+
url = "https://firebasestorage.googleapis.com/v0/b/retro-media-multi/o"
|
|
128
|
+
r = requests.get(url, headers=self.get_auth_header(), params={"prefix": f"media/{user_id}/{week}/", "delimiter": "/"})
|
|
129
|
+
try:
|
|
130
|
+
r.raise_for_status()
|
|
131
|
+
except requests.HTTPError as e:
|
|
132
|
+
print(f"Error: {r.text}")
|
|
133
|
+
raise
|
|
134
|
+
return r.json()
|
|
135
|
+
|
|
136
|
+
def get_filenames_in_folder(self, user_id, week):
|
|
137
|
+
data = self.list_files_in_folder(user_id, week)
|
|
138
|
+
items = data.get("items", [])
|
|
139
|
+
return [item["name"] for item in items] if items else []
|
|
140
|
+
|
|
141
|
+
def download_media_file(self, user_id, week, filename, output_path):
|
|
142
|
+
url = f"https://firebasestorage.googleapis.com/v0/b/retro-media-multi/o/media%2F{user_id}%2F{week}%2F{filename}?alt=media"
|
|
143
|
+
r = requests.get(url, headers=self.get_auth_header(), allow_redirects=True)
|
|
144
|
+
try:
|
|
145
|
+
r.raise_for_status()
|
|
146
|
+
except requests.HTTPError as e:
|
|
147
|
+
print(f"Error: {r.text}")
|
|
148
|
+
raise
|
|
149
|
+
with open(output_path, "wb") as f:
|
|
150
|
+
f.write(r.content)
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
def profile_weeks(self, user_id, verbose=False):
|
|
154
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/profileWeeks"
|
|
155
|
+
headers = {"content-type": "application/json", "Authorization": f"Bearer {self.access_token}"}
|
|
156
|
+
payload = {"data": {"uid": user_id}}
|
|
157
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
158
|
+
try:
|
|
159
|
+
r.raise_for_status()
|
|
160
|
+
except requests.HTTPError as e:
|
|
161
|
+
print(f"Error: {r.text}")
|
|
162
|
+
raise
|
|
163
|
+
r_json = r.json()
|
|
164
|
+
if verbose:
|
|
165
|
+
return r_json
|
|
166
|
+
return r_json["result"]
|
|
167
|
+
|
|
168
|
+
def set_username(self, username, verbose=False):
|
|
169
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/setUsername"
|
|
170
|
+
headers = {"content-type": "application/json", "Authorization": f"Bearer {self.access_token}"}
|
|
171
|
+
payload = {"data": {"username": username}}
|
|
172
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
173
|
+
try:
|
|
174
|
+
r.raise_for_status()
|
|
175
|
+
except requests.HTTPError as e:
|
|
176
|
+
print(f"Error: {r.text}")
|
|
177
|
+
raise
|
|
178
|
+
if not verbose:
|
|
179
|
+
return
|
|
180
|
+
return r.json()
|
|
181
|
+
|
|
182
|
+
def send_friend_request(self, user_id, verbose=False):
|
|
183
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/requestFriend"
|
|
184
|
+
headers = {"content-type": "application/json", "Authorization": f"Bearer {self.access_token}"}
|
|
185
|
+
payload = {"data": {"uid": user_id}}
|
|
186
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
187
|
+
try:
|
|
188
|
+
r.raise_for_status()
|
|
189
|
+
except requests.HTTPError as e:
|
|
190
|
+
print(f"Error: {r.text}")
|
|
191
|
+
raise
|
|
192
|
+
if not verbose:
|
|
193
|
+
return
|
|
194
|
+
return r.json()
|
|
195
|
+
|
|
196
|
+
def cancel_friend_request(self, user_id, verbose=False):
|
|
197
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/cancelFriendRequest"
|
|
198
|
+
headers = {"content-type": "application/json", "Authorization": f"Bearer {self.access_token}"}
|
|
199
|
+
payload = {"data": {"uid": user_id}}
|
|
200
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
201
|
+
try:
|
|
202
|
+
r.raise_for_status()
|
|
203
|
+
except requests.HTTPError as e:
|
|
204
|
+
print(f"Error: {r.text}")
|
|
205
|
+
raise
|
|
206
|
+
if not verbose:
|
|
207
|
+
return
|
|
208
|
+
return r.json()
|
|
209
|
+
|
|
210
|
+
def unfriend(self, user_id, verbose=False):
|
|
211
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/unfriend"
|
|
212
|
+
headers = {"content-type": "application/json", "Authorization": f"Bearer {self.access_token}"}
|
|
213
|
+
payload = {"data": {"uid": user_id}}
|
|
214
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
215
|
+
try:
|
|
216
|
+
r.raise_for_status()
|
|
217
|
+
except requests.HTTPError as e:
|
|
218
|
+
print(f"Error: {r.text}")
|
|
219
|
+
raise
|
|
220
|
+
if not verbose:
|
|
221
|
+
return
|
|
222
|
+
return r.json()
|
|
223
|
+
|
|
224
|
+
def get_people_you_may_also_know(self, user_id, verbose=False):
|
|
225
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/getPeopleYouMayAlsoKnow"
|
|
226
|
+
headers = {"content-type": "application/json", "Authorization": f"Bearer {self.access_token}"}
|
|
227
|
+
payload = {"data": {"uid": user_id}}
|
|
228
|
+
r = requests.post(url, headers=headers, json=payload)
|
|
229
|
+
try:
|
|
230
|
+
r.raise_for_status()
|
|
231
|
+
except requests.HTTPError as e:
|
|
232
|
+
print(f"Error: {r.text}")
|
|
233
|
+
raise
|
|
234
|
+
r_json = r.json()
|
|
235
|
+
if not verbose:
|
|
236
|
+
return r_json["result"]["peopleYouMayAlsoKnow"]
|
|
237
|
+
return r_json
|
|
238
|
+
|
|
239
|
+
def send_code_v2(self, phone_number, verbose=False):
|
|
240
|
+
url = "https://us-central1-retro-media.cloudfunctions.net/sendCodeV2"
|
|
241
|
+
headers = {"content-type": "application/json"}
|
|
242
|
+
|
|
243
|
+
def search_users(self, username, page=0): # figure out how paginating works/how to request a later page. get all pages if num > 0 (?)
|
|
244
|
+
url = "https://39g8v6v6qe-dsn.algolia.net/1/indexes/users/query"
|
|
245
|
+
headers = {"X-Algolia-API-Key": "574f0e95c4fbfe204867000799037e69", "X-Algolia-Application-Id": "39G8V6V6QE"}
|
|
246
|
+
r = requests.post(url, headers=headers, json={"query": username})
|
|
247
|
+
try:
|
|
248
|
+
r.raise_for_status()
|
|
249
|
+
except requests.HTTPError as e:
|
|
250
|
+
print(f"Error: {r.text}")
|
|
251
|
+
raise
|
|
252
|
+
return r.json()
|
|
253
|
+
|
|
254
|
+
def get_user_id_from_username(self, username):
|
|
255
|
+
results = self.search_users(username)
|
|
256
|
+
hits = results.get("hits", [])
|
|
257
|
+
return results[0].get("objectID") if hits else None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
retro_sdk
|