summaryrefslogtreecommitdiff
path: root/steamrelationships
diff options
context:
space:
mode:
Diffstat (limited to 'steamrelationships')
-rw-r--r--steamrelationships/sr.py90
1 files changed, 45 insertions, 45 deletions
diff --git a/steamrelationships/sr.py b/steamrelationships/sr.py
index 28b6b16..63122b4 100644
--- a/steamrelationships/sr.py
+++ b/steamrelationships/sr.py
@@ -8,14 +8,14 @@ CONST = _Const()
8 8
9class SteamRelationships: 9class SteamRelationships:
10 """A class that handles the querring of Steam's web api to request a user's friends list""" 10 """A class that handles the querring of Steam's web api to request a user's friends list"""
11 11
12 session: object = requests.Session() # Session object so repeated querries to steam's api use the same TCP connection 12 session: object = requests.Session() # Session object so repeated querries to steam's api use the same TCP connection
13 scanlist: dict = {} # To be populated by scan functions 13 scanlist: dict = {} # To be populated by scan functions
14 14
15 def __init__(self, webapikey: str, timeout: int = 30, timeout_retries: int = 5, reqdelay: float = 0.5, delayrand: int = 25, reqsafetybuffer: float = 0.9, reqjson: str = "requests.json") -> None: 15 def __init__(self, webapikey: str, timeout: int = 30, timeout_retries: int = 5, reqdelay: float = 0.5, delayrand: int = 25, reqsafetybuffer: float = 0.9, reqjson: str = "requests.json") -> None:
16 """ 16 """
17 (str) webapikey - A Steam "web api key" for grabbing friends lists (get one at https://steamcommunity.com/dev/apikey) 17 (str) webapikey - A Steam "web api key" for grabbing friends lists (get one at https://steamcommunity.com/dev/apikey)
18 18
19 (int) timeout (Default: 30) - The time in seconds to wait for a response from Steam before timing out 19 (int) timeout (Default: 30) - The time in seconds to wait for a response from Steam before timing out
20 (int) timeout_retries (Default: 5) - The number of times to retry a scan after a timeout 20 (int) timeout_retries (Default: 5) - The number of times to retry a scan after a timeout
21 (float) reqdelay (Default: 0.5) - The base amount of seconds to wait after each scan (to lessen the load on Steam's servers) 21 (float) reqdelay (Default: 0.5) - The base amount of seconds to wait after each scan (to lessen the load on Steam's servers)
@@ -40,14 +40,14 @@ class SteamRelationships:
40 def __str__(self) -> str: 40 def __str__(self) -> str:
41 return f"Vals:\n\twebapikey: {len(self.webapikey) > 0} (Actual value ommitted for security)\n\n\tTimeout: {self.timeout}\n\tTimeout Retries: {self.timeout_retries}\n\tRequest Delay: {self.reqdelay}\n\tRequest Delay Randomness Factor: +/- {self.delayrand}%\n\tRequest Safety Buffer: {self.reqsafetybuffer} ({self.reqsafetybuffer * CONST.STEAMAPI_MAXREQ} out of {CONST.STEAMAPI_MAXREQ} max requests)\n\tRequests Log Filepath: \"{self.reqjson}\"\n\n\tMost Recent Scan: {self.scanlist}" 41 return f"Vals:\n\twebapikey: {len(self.webapikey) > 0} (Actual value ommitted for security)\n\n\tTimeout: {self.timeout}\n\tTimeout Retries: {self.timeout_retries}\n\tRequest Delay: {self.reqdelay}\n\tRequest Delay Randomness Factor: +/- {self.delayrand}%\n\tRequest Safety Buffer: {self.reqsafetybuffer} ({self.reqsafetybuffer * CONST.STEAMAPI_MAXREQ} out of {CONST.STEAMAPI_MAXREQ} max requests)\n\tRequests Log Filepath: \"{self.reqjson}\"\n\n\tMost Recent Scan: {self.scanlist}"
42 42
43 def _readjsonfile(self, filepath: str = "") -> dict: 43 def __readjsonfile(self, filepath: str = "") -> dict:
44 """ 44 """
45 _readjsonfile(self, filepath: str = "") -> dict: Read the specified json file for previous requests 45 __readjsonfile(self, filepath: str = "") -> dict: Read the specified json file for previous requests
46 filepath: Path to json file 46 filepath: Path to json file
47 47
48 dict (return): The contents of the json file. Empty on error 48 dict (return): The contents of the json file. Empty on error
49 49
50 _readjsonfile will create an "empty" json file if the specified file at the filepath doesn't exist 50 __readjsonfile will create an "empty" json file if the specified file at the filepath doesn't exist
51 """ 51 """
52 if not filepath: 52 if not filepath:
53 filepath = self.reqjson 53 filepath = self.reqjson
@@ -63,31 +63,31 @@ class SteamRelationships:
63 63
64 # If the file does not exist, create one and slap an empty list in it 64 # If the file does not exist, create one and slap an empty list in it
65 except FileNotFoundError: 65 except FileNotFoundError:
66 print(f"[_readjsonfile] File {filepath} does not exist. Generating empty json file...") 66 print(f"[__readjsonfile] File {filepath} does not exist. Generating empty json file...")
67 try: 67 try:
68 with open(filepath, "w+") as newfile: 68 with open(filepath, "w+") as newfile:
69 json.dump({time.time_ns(): [0, []]}, newfile, indent=CONST.JSON_INDENT) 69 json.dump({time.time_ns(): [0, []]}, newfile, indent=CONST.JSON_INDENT)
70 newfile.close() 70 newfile.close()
71 71
72 return self._readjsonfile(filepath) 72 return self.__readjsonfile(filepath)
73 73
74 except Exception as e: 74 except Exception as e:
75 print(f"[_readjsonfile] Couldn't create new file ({e})") 75 print(f"[__readjsonfile] Couldn't create new file ({e})")
76 return {} 76 return {}
77 77
78 except Exception as e: 78 except Exception as e:
79 print(f"[_readjsonfile] Other unknown error occured ({e})") 79 print(f"[__readjsonfile] Other unknown error occured ({e})")
80 return {} 80 return {}
81 81
82 return final 82 return final
83 83
84 def _checkrequests(self, filename: str = "") -> int: 84 def __checkrequests(self, filename: str = "") -> int:
85 """ 85 """
86 _checkrequests(self, filename: str = "") -> int: Check the requests log to make sure Steam's request limit hasn't been passed 86 __checkrequests(self, filename: str = "") -> int: Check the requests log to make sure Steam's request limit hasn't been passed
87 filename: filepath to requests log 87 filename: filepath to requests log
88 int (return): The number of requests in the last 24 hours. -1 on error, 0 on too many requests, and >1 if a valid number of requests 88 int (return): The number of requests in the last 24 hours. -1 on error, 0 on too many requests, and >1 if a valid number of requests
89 89
90 _checkrequests will create a file at filepath if it doesn't exist. It will also never go over 100,000 requests, regardless of what reqsafetybuffer is 90 __checkrequests will create a file at filepath if it doesn't exist. It will also never go over 100,000 requests, regardless of what reqsafetybuffer is
91 """ 91 """
92 if not filename: 92 if not filename:
93 filename = self.reqjson 93 filename = self.reqjson
@@ -95,10 +95,10 @@ class SteamRelationships:
95 # Get the contents of the specified file 95 # Get the contents of the specified file
96 jsoncontents: dict = {} 96 jsoncontents: dict = {}
97 try: 97 try:
98 jsoncontents = self._readjsonfile(filename) 98 jsoncontents = self.__readjsonfile(filename)
99 99
100 except Exception as e: 100 except Exception as e:
101 print(f"[_checkrequests] Could not get the contents of file \"{filename}\" ({e})") 101 print(f"[__checkrequests] Could not get the contents of file \"{filename}\" ({e})")
102 return -1 102 return -1
103 103
104 # Check the current date. If over 1 day since last entry, add a new entry. Otherwise, edit the current day's entry [note, 1 day in nanoseconds = (8.64 * (10 ** 13)) ] 104 # Check the current date. If over 1 day since last entry, add a new entry. Otherwise, edit the current day's entry [note, 1 day in nanoseconds = (8.64 * (10 ** 13)) ]
@@ -114,7 +114,7 @@ class SteamRelationships:
114 jsoncontents[list(jsoncontents.keys())[-1]][1].append(checktime) 114 jsoncontents[list(jsoncontents.keys())[-1]][1].append(checktime)
115 115
116 else: 116 else:
117 print(f"[_checkrequests] Daily request limit reached ({currentreqs}/{CONST.STEAMAPI_MAXREQ * self.reqsafetybuffer}). Please try again tomorrow, or increase \"reqsafetybuffer\" (currently: {self.reqsafetybuffer})") 117 print(f"[__checkrequests] Daily request limit reached ({currentreqs}/{CONST.STEAMAPI_MAXREQ * self.reqsafetybuffer}). Please try again tomorrow, or increase \"reqsafetybuffer\" (currently: {self.reqsafetybuffer})")
118 return 0 118 return 0
119 119
120 # Update the json file 120 # Update the json file
@@ -124,28 +124,28 @@ class SteamRelationships:
124 jsonfile.close() 124 jsonfile.close()
125 125
126 except Exception as e: 126 except Exception as e:
127 print(f"[_checkrequests] Could not update json file ({e})") 127 print(f"[__checkrequests] Could not update json file ({e})")
128 return -1 128 return -1
129 129
130 return jsoncontents[list(jsoncontents.keys())[-1]][0] 130 return jsoncontents[list(jsoncontents.keys())[-1]][0]
131 131
132 132
133 133
134 def _getFriendsList(self, steamid64: str, _retries: int = 0) -> dict: 134 def __getFriendsList(self, steamid64: str, _retries: int = 0) -> dict:
135 """ 135 """
136 _getFriendsList(self, steamid64: str, _retries: int = 0) -> dict: Send a request to the Steam Web API to get a user's friends list 136 __getFriendsList(self, steamid64: str, _retries: int = 0) -> dict: Send a request to the Steam Web API to get a user's friends list
137 steamid64: A Steam User's id, in the steamid64 format 137 steamid64: A Steam User's id, in the steamid64 format
138 _retries: An internal value used to limit the number of retries after a timeout. Increments by one automatically on every timeout 138 _retries: An internal value used to limit the number of retries after a timeout. Increments by one automatically on every timeout
139 139
140 dict (return): The json representation of Steam's response. Empty on error 140 dict (return): The json representation of Steam's response. Empty on error
141 """ 141 """
142 if not steamid64: 142 if not steamid64:
143 print("[_getFriendsList] No steamid64 given") 143 print("[__getFriendsList] No steamid64 given")
144 return {} 144 return {}
145 145
146 # Make sure we haven't gone over steam's daily max 146 # Make sure we haven't gone over steam's daily max
147 if self._checkrequests() == 0: 147 if self.__checkrequests() == 0:
148 print("[_getFriendsList] Max requests reached, refusing to contact steam") 148 print("[__getFriendsList] Max requests reached, refusing to contact steam")
149 return {} 149 return {}
150 150
151 url: str = "https://api.steampowered.com/ISteamUser/GetFriendList/v0001/" 151 url: str = "https://api.steampowered.com/ISteamUser/GetFriendList/v0001/"
@@ -157,24 +157,24 @@ class SteamRelationships:
157 result = self.session.get(url, params=options, timeout=self.timeout) 157 result = self.session.get(url, params=options, timeout=self.timeout)
158 158
159 except requests.exceptions.Timeout: 159 except requests.exceptions.Timeout:
160 print(f"[_getFriendsList] Request timed out (No response for {self.timeout} seconds)") 160 print(f"[__getFriendsList] Request timed out (No response for {self.timeout} seconds)")
161 if _retries <= self.timeout_retries: 161 if _retries <= self.timeout_retries:
162 print(f"[_getFriendsList] Retrying request... (Attempt {_retries}/{self.timeout_retries})") 162 print(f"[__getFriendsList] Retrying request... (Attempt {_retries}/{self.timeout_retries})")
163 return self._getFriendsList(steamid64, _retries + 1) 163 return self.__getFriendsList(steamid64, _retries + 1)
164 164
165 print("[_getFriendsList] Retry limit reached") 165 print("[__getFriendsList] Retry limit reached")
166 return {} 166 return {}
167 167
168 except Exception as e: 168 except Exception as e:
169 print(f"[_getFriendsList] Other error in contacting steam ({e})") 169 print(f"[__getFriendsList] Other error in contacting steam ({e})")
170 return {} 170 return {}
171 171
172 # Error out on request error 172 # Error out on request error
173 if result.status_code != requests.codes.ok: 173 if result.status_code != requests.codes.ok:
174 print(f"[_getFriendsList] Got bad status code (Requested id: {steamid64}, status: {result.status_code})", end="") 174 print(f"[__getFriendsList] Got bad status code (Requested id: {steamid64}, status: {result.status_code})", end="")
175 if result.status_code == 401: # Steam returns a 401 on profiles with private friends lists 175 if result.status_code == 401: # Steam returns a 401 on profiles with private friends lists
176 print(f". It is likely that the requested id has a private profile / private friends list", end="") 176 print(f". It is likely that the requested id has a private profile / private friends list", end="")
177 177
178 print("\n", end="") 178 print("\n", end="")
179 return {} 179 return {}
180 180
@@ -184,25 +184,25 @@ class SteamRelationships:
184 resultjson = result.json() 184 resultjson = result.json()
185 185
186 except requests.exceptions.JSONDecodeError: 186 except requests.exceptions.JSONDecodeError:
187 print("[_getFriendsList] Could not decode json response for some reason") 187 print("[__getFriendsList] Could not decode json response for some reason")
188 return {} 188 return {}
189 189
190 except Exception as e: 190 except Exception as e:
191 print(f"[_getFriendsList] Unknown error in getting json response ({e})") 191 print(f"[__getFriendsList] Unknown error in getting json response ({e})")
192 return {} 192 return {}
193 193
194 return resultjson 194 return resultjson
195 195
196 def _parseFriendsList(self, friendsdict: dict) -> list: 196 def __parseFriendsList(self, friendsdict: dict) -> list:
197 """ 197 """
198 _parseFriendsList(self, friendsdict: dict) -> list: Parse a response from Steam and extract a user's friend's Steam IDs 198 __parseFriendsList(self, friendsdict: dict) -> list: Parse a response from Steam and extract a user's friend's Steam IDs
199 friendsdict: The return value of _getFriendsList 199 friendsdict: The return value of __getFriendsList
200 200
201 list (return): The steamid64's of a user's friends. Empty on error 201 list (return): The steamid64's of a user's friends. Empty on error
202 """ 202 """
203 203
204 if not friendsdict: 204 if not friendsdict:
205 print("[_parseFriendsList] Empty friends dict given") 205 print("[__parseFriendsList] Empty friends dict given")
206 return [] 206 return []
207 207
208 208
@@ -212,7 +212,7 @@ class SteamRelationships:
212 people.append(f'{friend["steamid"]}') 212 people.append(f'{friend["steamid"]}')
213 213
214 except Exception as e: 214 except Exception as e:
215 print("[_parseFriendsList] Error parsing friendsdict ({e})") 215 print("[__parseFriendsList] Error parsing friendsdict ({e})")
216 people.clear() 216 people.clear()
217 217
218 return people 218 return people
@@ -229,7 +229,7 @@ class SteamRelationships:
229 (dict) {steamid64: [friendID1, friendID2, ...]} - A dict with a single key, that of the scanned user, which maps to a list of the users's friends 229 (dict) {steamid64: [friendID1, friendID2, ...]} - A dict with a single key, that of the scanned user, which maps to a list of the users's friends
230 230
231 ''' 231 '''
232 self.scanlist = {steamid64: self._parseFriendsList(self._getFriendsList(steamid64))} 232 self.scanlist = {steamid64: self.__parseFriendsList(self.__getFriendsList(steamid64))}
233 return self.scanlist 233 return self.scanlist
234 234
235 def recursivescan(self, steamid64: str, recurselevel: int = 2) -> dict: 235 def recursivescan(self, steamid64: str, recurselevel: int = 2) -> dict:
@@ -244,12 +244,12 @@ class SteamRelationships:
244 RETURN VALUES: 244 RETURN VALUES:
245 (dict) EMPTY - Some catastrophic error has occured and no scan could be started 245 (dict) EMPTY - Some catastrophic error has occured and no scan could be started
246 (dict) { 246 (dict) {
247 steamid64: 247 steamid64:
248 [friend1id, friend2id, friend3id, ...], 248 [friend1id, friend2id, friend3id, ...],
249 friend1id: 249 friend1id:
250 [other_friend, other_friend, ...], 250 [other_friend, other_friend, ...],
251 friend2id: 251 friend2id:
252 [other_friend, other_friend, ...], 252 [other_friend, other_friend, ...],
253 friend3id: 253 friend3id:
254 [other_friend, other_friend, ...], 254 [other_friend, other_friend, ...],
255 ... 255 ...
@@ -276,7 +276,7 @@ class SteamRelationships:
276 276
277 sleepytime: float = abs(random.randrange(100 - self.delayrand, 100 + self.delayrand) / 100 * self.reqdelay) 277 sleepytime: float = abs(random.randrange(100 - self.delayrand, 100 + self.delayrand) / 100 * self.reqdelay)
278 time.sleep(sleepytime) 278 time.sleep(sleepytime)
279 279
280 testlist.update(tempdict) 280 testlist.update(tempdict)
281 tempdict.clear() 281 tempdict.clear()
282 282