diff --git a/sherlock/resources/data.json b/sherlock/resources/data.json index d953802b9..c0ad9db1f 100644 --- a/sherlock/resources/data.json +++ b/sherlock/resources/data.json @@ -1256,6 +1256,69 @@ "urlMain": "https://launchpad.net/", "username_claimed": "blue" }, + "LoLKr": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/kr/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "NS Callme#KR1" + }, + "LoLEune": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/eune/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "NotPurple#EUNE" + }, + "LoLEuw": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/euw/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "normalus bahuras#EUW" + }, + "LoLNa": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/na/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "Sheiden#0001" + }, + "LoLVn": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/vn/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "Shirou#2K5" + }, + "LoLBr": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/br/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "frosty#KR3" + }, + "LoLTr": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/tr/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "Hide on bush#MBM0" + }, + "LoLJp": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/jp/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "Ninja of Ninjas#JP1" + }, + "LoLOce": { + "errorType": "status_code", + "isTagRequired": true, + "url": "https://www.leagueofgraphs.com/summoner/oce/{}-<>", + "urlMain": "https://www.leagueofgraphs.com/", + "username_claimed": "Stop here#OCE" + }, "LeetCode": { "errorType": "status_code", "url": "https://leetcode.com/{}", diff --git a/sherlock/resources/data.schema.json b/sherlock/resources/data.schema.json index 4453500c0..b83ce590c 100644 --- a/sherlock/resources/data.schema.json +++ b/sherlock/resources/data.schema.json @@ -18,6 +18,7 @@ "username_claimed": { "type": "string" }, "regexCheck": { "type": "string" }, "isNSFW": { "type": "boolean" }, + "isTagRequired": { "type": "boolean"}, "headers": { "type": "object" }, "request_payload": { "type": "object" }, "__comment__": { diff --git a/sherlock/sherlock.py b/sherlock/sherlock.py index 4b7a27988..81c4a9c38 100644 --- a/sherlock/sherlock.py +++ b/sherlock/sherlock.py @@ -137,13 +137,16 @@ def get_response(request_future, error_type, social_network): return response, error_context, exception_text -def interpolate_string(input_object, username): +def interpolate_string(input_object, username, has_tag): if isinstance(input_object, str): + if has_tag and '#' in username: + username, tag = username.split('#') + input_object = input_object.replace("<>", tag) return input_object.replace("{}", username) elif isinstance(input_object, dict): - return {k: interpolate_string(v, username) for k, v in input_object.items()} + return {k: interpolate_string(v, username, has_tag) for k, v in input_object.items()} elif isinstance(input_object, list): - return [interpolate_string(i, username) for i in input_object] + return [interpolate_string(i, username, has_tag) for i in input_object] return input_object @@ -168,6 +171,7 @@ def sherlock( username, site_data, query_notify: QueryNotify, + has_tag: bool = False, tor: bool = False, unique_tor: bool = False, proxy=None, @@ -253,8 +257,13 @@ def sherlock( # Override/append any extra headers required by a given site. headers.update(net_info["headers"]) + # if --tag flag is set but username doesn't contain tag, no need to make any requests + if has_tag and '#' not in username: + print("Flag --tag was set, but the tag wasn't provided. Please use " + "'username#tag' format") + return results_total # URL of user on site (if it exists) - url = interpolate_string(net_info["url"], username.replace(' ', '%20')) + url = interpolate_string(net_info["url"], username.replace(' ', '%20'), has_tag) # Don't make request if username is invalid for the site regex_check = net_info.get("regexCheck") @@ -288,7 +297,7 @@ def sherlock( raise RuntimeError(f"Unsupported request_method for {url}") if request_payload is not None: - request_payload = interpolate_string(request_payload, username) + request_payload = interpolate_string(request_payload, username, has_tag) if url_probe is None: # Probe URL is normal one seen by people out on the web. @@ -296,7 +305,7 @@ def sherlock( else: # There is a special URL for probing existence separate # from where the user profile normally can be found. - url_probe = interpolate_string(url_probe, username) + url_probe = interpolate_string(url_probe, username, has_tag) if request is None: if net_info["errorType"] == "status_code": @@ -666,6 +675,15 @@ def main(): help="Include checking of NSFW sites from default list.", ) + parser.add_argument( + "--tag", + action="store_true", + dest="tag", + default=False, + help="Search for a user name including a user tag (e.g. League of Legends, Valorant). " + "Username has to be entered in format 'username#tag'." + ) + args = parser.parse_args() # If the user presses CTRL-C, exit gracefully without throwing errors @@ -737,6 +755,11 @@ def main(): if not args.nsfw: sites.remove_nsfw_sites(do_not_remove=args.site_list) + if args.tag: + sites.filter_websites_based_on_tag(args.site_list, tag_required=True) + else: + sites.filter_websites_based_on_tag(args.site_list, tag_required=False) + # Create original dictionary from SitesInformation() object. # Eventually, the rest of the code will be updated to use the new object # directly, but this will glue the two pieces together. @@ -760,7 +783,10 @@ def main(): site_missing.append(f"'{site}'") if site_missing: - print(f"Error: Desired sites not found: {', '.join(site_missing)}.") + if args.tag: + print(f"Error: '--tag' flag was set, but {', '.join(site_missing)} usernames don't have tags.") + else: + print(f"Error: Desired sites not found: {', '.join(site_missing)}.") if not site_data: sys.exit(1) @@ -783,6 +809,7 @@ def main(): username, site_data, query_notify, + args.tag, tor=args.tor, unique_tor=args.unique_tor, proxy=args.proxy, diff --git a/sherlock/sites.py b/sherlock/sites.py index 112b6d023..15cd7fab8 100644 --- a/sherlock/sites.py +++ b/sherlock/sites.py @@ -9,7 +9,7 @@ class SiteInformation: def __init__(self, name, url_home, url_username_format, username_claimed, - information, is_nsfw, username_unclaimed=secrets.token_urlsafe(10)): + information, is_nsfw, is_tag_required, username_unclaimed=secrets.token_urlsafe(10)): """Create Site Information Object. Contains information about a specific website. @@ -55,6 +55,7 @@ def __init__(self, name, url_home, url_username_format, username_claimed, self.username_unclaimed = secrets.token_urlsafe(32) self.information = information self.is_nsfw = is_nsfw + self.is_tag_required = is_tag_required return @@ -67,7 +68,7 @@ def __str__(self): Return Value: Nicely formatted string to get information about this object. """ - + return f"{self.name} ({self.url_home})" @@ -152,7 +153,7 @@ def __init__(self, data_file_path=None): raise FileNotFoundError(f"Problem while attempting to access " f"data file '{data_file_path}'." ) - + site_data.pop('$schema', None) self.sites = {} @@ -167,7 +168,8 @@ def __init__(self, data_file_path=None): site_data[site_name]["url"], site_data[site_name]["username_claimed"], site_data[site_name], - site_data[site_name].get("isNSFW",False) + site_data[site_name].get("isNSFW", False), + site_data[site_name].get("isTagRequired", False) ) except KeyError as error: @@ -179,6 +181,30 @@ def __init__(self, data_file_path=None): return + def filter_websites_based_on_tag(self, site_list, tag_required): + """ + Filter websites based on '--tag' flag, if it's provided, the result is a list + of websites that have isTagRequired set to True, and if '--tag' is not provided, + the result is a list of all websites that don't have isTagRequired set to True. + + Keyword Arguments: + self -- This object. + site_list -- sites provided with '--site' flag + tag_required -- boolean value, True if '--tag' flag is provided + + Return Value: + None + """ + sites_requiring_tag = set(site for site in self.sites if self.sites[site].is_tag_required) + if not tag_required and sites_requiring_tag and site_list: + print("Some websites require a tag, but the tag flag is not set.") + filtered_sites = {} + for site in self.sites: + if (tag_required and site in sites_requiring_tag) or \ + (not tag_required and site not in sites_requiring_tag): + filtered_sites[site] = self.sites[site] + self.sites = filtered_sites + def remove_nsfw_sites(self, do_not_remove: list = []): """ Remove NSFW sites from the sites, if isNSFW flag is true for site @@ -194,7 +220,7 @@ def remove_nsfw_sites(self, do_not_remove: list = []): for site in self.sites: if self.sites[site].is_nsfw and site.casefold() not in do_not_remove: continue - sites[site] = self.sites[site] + sites[site] = self.sites[site] self.sites = sites def site_name_list(self):