From cc682f520f9941076b1515526b32fa9250364b5a Mon Sep 17 00:00:00 2001 From: NW/RL Date: Fri, 12 Apr 2024 16:52:46 -0500 Subject: Fix scanlist, update documentation --- steamrelationships/sr.py | 73 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 14 deletions(-) (limited to 'steamrelationships/sr.py') 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 CONST = _Const() class SteamRelationships: + """A class that handles the querring of Steam's web api to request a user's friends list""" + session: object = requests.Session() # Session object so repeated querries to steam's api use the same TCP connection - scanlist: dict = {} # To be populated by recursivescan() + scanlist: dict = {} # To be populated by scan functions 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: """ - """ # I can't make this a formatted string :sob: + (str) webapikey - A Steam "web api key" for grabbing friends lists (get one at https://steamcommunity.com/dev/apikey) + + (int) timeout (Default: 30) - The time in seconds to wait for a response from Steam before timing out + (int) timeout_retries (Default: 5) - The number of times to retry a scan after a timeout + (float) reqdelay (Default: 0.5) - The base amount of seconds to wait after each scan (to lessen the load on Steam's servers) + (int) delayrand (Default: 25) - The maximum percent up/down to add/subtract to the request delay + (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 + (str) reqjson (Default: "requests.json") - The path to a .json file that stores request numbers + + If a value is entered and is of the incorrect type / can't be automatically converted, SteamRelationships will raise a type error + All numerical values should be positive, and will be made positive automatically if otherwise + delayrand will be truncated to between [0, 99] (inclusive) if over 100 + """ # This used to be in a try-except block, but I decided that it should error out on a proper error self.webapikey: str = str(webapikey) @@ -23,8 +37,18 @@ class SteamRelationships: self.reqsafetybuffer: float = float(abs(reqsafetybuffer)) self.reqjson: str = str(reqjson) + def __str__(self) -> str: + 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}" def _readjsonfile(self, filepath: str = "") -> dict: + """ + _readjsonfile(self, filepath: str = "") -> dict: Read the specified json file for previous requests + filepath: Path to json file + + dict (return): The contents of the json file. Empty on error + + _readjsonfile will create an "empty" json file if the specified file at the filepath doesn't exist + """ if not filepath: filepath = self.reqjson @@ -58,6 +82,13 @@ class SteamRelationships: return final def _checkrequests(self, filename: str = "") -> int: + """ + _checkrequests(self, filename: str = "") -> int: Check the requests log to make sure Steam's request limit hasn't been passed + filename: filepath to requests log + 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 + + _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 + """ if not filename: filename = self.reqjson @@ -100,7 +131,14 @@ class SteamRelationships: - def _getFriendsList(self, steamid64: str, retrys: int = 0) -> dict: + def _getFriendsList(self, steamid64: str, _retries: int = 0) -> dict: + """ + _getFriendsList(self, steamid64: str, _retries: int = 0) -> dict: Send a request to the Steam Web API to get a user's friends list + steamid64: A Steam User's id, in the steamid64 format + _retries: An internal value used to limit the number of retries after a timeout. Increments by one automatically on every timeout + + dict (return): The json representation of Steam's response. Empty on error + """ if not steamid64: print("[_getFriendsList] No steamid64 given") return {} @@ -120,9 +158,9 @@ class SteamRelationships: except requests.exceptions.Timeout: print(f"[_getFriendsList] Request timed out (No response for {self.timeout} seconds)") - if retrys <= self.timeout_retries: - print(f"[_getFriendsList] Retrying request... (Attempt {retrys}/{self.timeout_retries})") - return self._getFriendsList(steamid64, retrys + 1) + if _retries <= self.timeout_retries: + print(f"[_getFriendsList] Retrying request... (Attempt {_retries}/{self.timeout_retries})") + return self._getFriendsList(steamid64, _retries + 1) print("[_getFriendsList] Retry limit reached") return {} @@ -156,6 +194,13 @@ class SteamRelationships: return resultjson def _parseFriendsList(self, friendsdict: dict) -> list: + """ + _parseFriendsList(self, friendsdict: dict) -> list: Parse a response from Steam and extract a user's friend's Steam IDs + friendsdict: The return value of _getFriendsList + + list (return): The steamid64's of a user's friends. Empty on error + """ + if not friendsdict: print("[_parseFriendsList] Empty friends dict given") return [] @@ -184,7 +229,8 @@ class SteamRelationships: (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 ''' - return {steamid64: self._parseFriendsList(self._getFriendsList(steamid64))} + self.scanlist = {steamid64: self._parseFriendsList(self._getFriendsList(steamid64))} + return self.scanlist def recursivescan(self, steamid64: str, recurselevel: int = 2) -> dict: ''' @@ -193,6 +239,7 @@ class SteamRelationships: PARAMS: (str) steamid64 - The starting user to scan (int) recurselevel - The number of recursive scans to complete + > Note: 0 is equivalent to a basic scan; 1 scans the specified user, then the user's friends; etc. RETURN VALUES: (dict) EMPTY - Some catastrophic error has occured and no scan could be started @@ -203,17 +250,15 @@ class SteamRelationships: [other_friend, other_friend, ...], friend2id: [other_friend, other_friend, ...], + friend3id: + [other_friend, other_friend, ...], ... } - A dict containing the starting steamid, then the friends contained in the original scan with the results of their scan NOTE: - 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 - + 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 TLDR: recursivescan is exponential and you will reach 100,000 requests very quickly if you recurse greater than 3 ''' @@ -235,5 +280,5 @@ class SteamRelationships: testlist.update(tempdict) tempdict.clear() - self.scanlist.update(testlist) - return testlist \ No newline at end of file + self.scanlist = testlist + return self.scanlist \ No newline at end of file -- cgit v1.2.3