diff options
| author | NW/RL <NWRL@dabikers.online> | 2024-04-12 16:52:46 -0500 |
|---|---|---|
| committer | NW/RL <NWRL@dabikers.online> | 2024-04-12 16:52:46 -0500 |
| commit | cc682f520f9941076b1515526b32fa9250364b5a (patch) | |
| tree | 05218d079656a1514b8e63b44716700fa2ba6d54 /steamrelationships | |
| parent | d6832a62fe0272ba725b3d5ceab64b5fb3a07276 (diff) | |
Fix scanlist, update documentation
Diffstat (limited to 'steamrelationships')
| -rw-r--r-- | steamrelationships/__init__.py | 2 | ||||
| -rw-r--r-- | steamrelationships/sr.py | 73 |
2 files changed, 61 insertions, 14 deletions
diff --git a/steamrelationships/__init__.py b/steamrelationships/__init__.py index 8c1715a..7862897 100644 --- a/steamrelationships/__init__.py +++ b/steamrelationships/__init__.py | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | __all__ = ["sr", "constants"] | ||
| 2 | |||
| 1 | from steamrelationships.sr import SteamRelationships | 3 | from steamrelationships.sr import SteamRelationships |
| 2 | from steamrelationships.constants import _Const | 4 | from steamrelationships.constants import _Const |
| 3 | CONST = _Const() \ No newline at end of file | 5 | CONST = _Const() \ No newline at end of file |
diff --git a/steamrelationships/sr.py b/steamrelationships/sr.py index 1cf9c2e..28b6b16 100644 --- a/steamrelationships/sr.py +++ b/steamrelationships/sr.py | |||
| @@ -7,12 +7,26 @@ from steamrelationships.constants import _Const | |||
| 7 | CONST = _Const() | 7 | CONST = _Const() |
| 8 | 8 | ||
| 9 | class SteamRelationships: | 9 | class SteamRelationships: |
| 10 | """A class that handles the querring of Steam's web api to request a user's friends list""" | ||
| 11 | |||
| 10 | 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 |
| 11 | scanlist: dict = {} # To be populated by recursivescan() | 13 | scanlist: dict = {} # To be populated by scan functions |
| 12 | 14 | ||
| 13 | 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: |
| 14 | """ | 16 | """ |
| 15 | """ # I can't make this a formatted string :sob: | 17 | (str) webapikey - A Steam "web api key" for grabbing friends lists (get one at https://steamcommunity.com/dev/apikey) |
| 18 | |||
| 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 | ||
| 21 | (float) reqdelay (Default: 0.5) - The base amount of seconds to wait after each scan (to lessen the load on Steam's servers) | ||
| 22 | (int) delayrand (Default: 25) - The maximum percent up/down to add/subtract to the request delay | ||
| 23 | (float) reqsafetybuffer (Default: 0.9) - A multiplier applied to Steam's max API request limit of 100,000. At 0.9, the new limit is 90,000 | ||
| 24 | (str) reqjson (Default: "requests.json") - The path to a .json file that stores request numbers | ||
| 25 | |||
| 26 | If a value is entered and is of the incorrect type / can't be automatically converted, SteamRelationships will raise a type error | ||
| 27 | All numerical values should be positive, and will be made positive automatically if otherwise | ||
| 28 | delayrand will be truncated to between [0, 99] (inclusive) if over 100 | ||
| 29 | """ | ||
| 16 | 30 | ||
| 17 | # This used to be in a try-except block, but I decided that it should error out on a proper error | 31 | # This used to be in a try-except block, but I decided that it should error out on a proper error |
| 18 | self.webapikey: str = str(webapikey) | 32 | self.webapikey: str = str(webapikey) |
| @@ -23,8 +37,18 @@ class SteamRelationships: | |||
| 23 | self.reqsafetybuffer: float = float(abs(reqsafetybuffer)) | 37 | self.reqsafetybuffer: float = float(abs(reqsafetybuffer)) |
| 24 | self.reqjson: str = str(reqjson) | 38 | self.reqjson: str = str(reqjson) |
| 25 | 39 | ||
| 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}" | ||
| 26 | 42 | ||
| 27 | def _readjsonfile(self, filepath: str = "") -> dict: | 43 | def _readjsonfile(self, filepath: str = "") -> dict: |
| 44 | """ | ||
| 45 | _readjsonfile(self, filepath: str = "") -> dict: Read the specified json file for previous requests | ||
| 46 | filepath: Path to json file | ||
| 47 | |||
| 48 | dict (return): The contents of the json file. Empty on error | ||
| 49 | |||
| 50 | _readjsonfile will create an "empty" json file if the specified file at the filepath doesn't exist | ||
| 51 | """ | ||
| 28 | if not filepath: | 52 | if not filepath: |
| 29 | filepath = self.reqjson | 53 | filepath = self.reqjson |
| 30 | 54 | ||
| @@ -58,6 +82,13 @@ class SteamRelationships: | |||
| 58 | return final | 82 | return final |
| 59 | 83 | ||
| 60 | def _checkrequests(self, filename: str = "") -> int: | 84 | def _checkrequests(self, filename: str = "") -> int: |
| 85 | """ | ||
| 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 | ||
| 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 | |||
| 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 | """ | ||
| 61 | if not filename: | 92 | if not filename: |
| 62 | filename = self.reqjson | 93 | filename = self.reqjson |
| 63 | 94 | ||
| @@ -100,7 +131,14 @@ class SteamRelationships: | |||
| 100 | 131 | ||
| 101 | 132 | ||
| 102 | 133 | ||
| 103 | def _getFriendsList(self, steamid64: str, retrys: int = 0) -> dict: | 134 | def _getFriendsList(self, steamid64: str, _retries: int = 0) -> dict: |
| 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 | ||
| 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 | ||
| 139 | |||
| 140 | dict (return): The json representation of Steam's response. Empty on error | ||
| 141 | """ | ||
| 104 | if not steamid64: | 142 | if not steamid64: |
| 105 | print("[_getFriendsList] No steamid64 given") | 143 | print("[_getFriendsList] No steamid64 given") |
| 106 | return {} | 144 | return {} |
| @@ -120,9 +158,9 @@ class SteamRelationships: | |||
| 120 | 158 | ||
| 121 | except requests.exceptions.Timeout: | 159 | except requests.exceptions.Timeout: |
| 122 | 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)") |
| 123 | if retrys <= self.timeout_retries: | 161 | if _retries <= self.timeout_retries: |
| 124 | print(f"[_getFriendsList] Retrying request... (Attempt {retrys}/{self.timeout_retries})") | 162 | print(f"[_getFriendsList] Retrying request... (Attempt {_retries}/{self.timeout_retries})") |
| 125 | return self._getFriendsList(steamid64, retrys + 1) | 163 | return self._getFriendsList(steamid64, _retries + 1) |
| 126 | 164 | ||
| 127 | print("[_getFriendsList] Retry limit reached") | 165 | print("[_getFriendsList] Retry limit reached") |
| 128 | return {} | 166 | return {} |
| @@ -156,6 +194,13 @@ class SteamRelationships: | |||
| 156 | return resultjson | 194 | return resultjson |
| 157 | 195 | ||
| 158 | def _parseFriendsList(self, friendsdict: dict) -> list: | 196 | def _parseFriendsList(self, friendsdict: dict) -> list: |
| 197 | """ | ||
| 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 | ||
| 200 | |||
| 201 | list (return): The steamid64's of a user's friends. Empty on error | ||
| 202 | """ | ||
| 203 | |||
| 159 | if not friendsdict: | 204 | if not friendsdict: |
| 160 | print("[_parseFriendsList] Empty friends dict given") | 205 | print("[_parseFriendsList] Empty friends dict given") |
| 161 | return [] | 206 | return [] |
| @@ -184,7 +229,8 @@ class SteamRelationships: | |||
| 184 | (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 |
| 185 | 230 | ||
| 186 | ''' | 231 | ''' |
| 187 | return {steamid64: self._parseFriendsList(self._getFriendsList(steamid64))} | 232 | self.scanlist = {steamid64: self._parseFriendsList(self._getFriendsList(steamid64))} |
| 233 | return self.scanlist | ||
| 188 | 234 | ||
| 189 | def recursivescan(self, steamid64: str, recurselevel: int = 2) -> dict: | 235 | def recursivescan(self, steamid64: str, recurselevel: int = 2) -> dict: |
| 190 | ''' | 236 | ''' |
| @@ -193,6 +239,7 @@ class SteamRelationships: | |||
| 193 | PARAMS: | 239 | PARAMS: |
| 194 | (str) steamid64 - The starting user to scan | 240 | (str) steamid64 - The starting user to scan |
| 195 | (int) recurselevel - The number of recursive scans to complete | 241 | (int) recurselevel - The number of recursive scans to complete |
| 242 | > Note: 0 is equivalent to a basic scan; 1 scans the specified user, then the user's friends; etc. | ||
| 196 | 243 | ||
| 197 | RETURN VALUES: | 244 | RETURN VALUES: |
| 198 | (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 |
| @@ -203,17 +250,15 @@ class SteamRelationships: | |||
| 203 | [other_friend, other_friend, ...], | 250 | [other_friend, other_friend, ...], |
| 204 | friend2id: | 251 | friend2id: |
| 205 | [other_friend, other_friend, ...], | 252 | [other_friend, other_friend, ...], |
| 253 | friend3id: | ||
| 254 | [other_friend, other_friend, ...], | ||
| 206 | ... | 255 | ... |
| 207 | } | 256 | } |
| 208 | 257 | ||
| 209 | - A dict containing the starting steamid, then the friends contained in the original scan with the results of their scan | 258 | - A dict containing the starting steamid, then the friends contained in the original scan with the results of their scan |
| 210 | 259 | ||
| 211 | NOTE: | 260 | NOTE: |
| 212 | Please do not use a value greater than 3. While theoretically any value works, due to the exponential | 261 | Please do not use a value greater than 3. While theoretically any value works, due to the exponential nature of friendship relations, you will very quickly spam Steam with tens of thousands of requests. If this concept is unfamiliar to you, please take a quick glance at the Wikipedia page for "six degress of separation": https://en.wikipedia.org/wiki/Six_degrees_of_separation |
| 213 | nature of friendship relations, you will very quickly spam Steam with tens of thousands of requests. | ||
| 214 | If this concept is unfamiliar to you, please take a quick glance at the Wikipedia page for | ||
| 215 | "six degress of separation": https://en.wikipedia.org/wiki/Six_degrees_of_separation | ||
| 216 | |||
| 217 | 262 | ||
| 218 | TLDR: recursivescan is exponential and you will reach 100,000 requests very quickly if you recurse greater than 3 | 263 | TLDR: recursivescan is exponential and you will reach 100,000 requests very quickly if you recurse greater than 3 |
| 219 | ''' | 264 | ''' |
| @@ -235,5 +280,5 @@ class SteamRelationships: | |||
| 235 | testlist.update(tempdict) | 280 | testlist.update(tempdict) |
| 236 | tempdict.clear() | 281 | tempdict.clear() |
| 237 | 282 | ||
| 238 | self.scanlist.update(testlist) | 283 | self.scanlist = testlist |
| 239 | return testlist \ No newline at end of file | 284 | return self.scanlist \ No newline at end of file |
