user-scanner 1.0.7.0__py3-none-any.whl → 1.0.8.1__py3-none-any.whl
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.
- user_scanner/community/__init__.py +1 -0
- user_scanner/community/stackoverflow.py +35 -0
- user_scanner/core/orchestrator.py +33 -30
- user_scanner/core/result.py +79 -0
- user_scanner/creator/hashnode.py +8 -13
- user_scanner/creator/itch_io.py +0 -5
- user_scanner/creator/medium.py +7 -12
- user_scanner/creator/patreon.py +0 -6
- user_scanner/creator/producthunt.py +31 -26
- user_scanner/dev/github.py +19 -2
- user_scanner/dev/gitlab.py +4 -3
- user_scanner/dev/huggingface.py +19 -0
- user_scanner/dev/npmjs.py +9 -25
- user_scanner/donation/buymeacoffee.py +0 -3
- user_scanner/gaming/chess_com.py +4 -9
- user_scanner/gaming/minecraft.py +0 -5
- user_scanner/gaming/monkeytype.py +4 -10
- user_scanner/gaming/osu.py +0 -5
- user_scanner/gaming/roblox.py +14 -9
- user_scanner/gaming/steam.py +6 -9
- user_scanner/social/bluesky.py +7 -13
- user_scanner/social/discord.py +6 -9
- user_scanner/social/mastodon.py +0 -6
- user_scanner/social/pinterest.py +4 -4
- user_scanner/social/reddit.py +4 -3
- user_scanner/social/soundcloud.py +43 -0
- user_scanner/social/telegram.py +6 -6
- user_scanner/social/x.py +10 -20
- user_scanner/social/youtube.py +17 -26
- user_scanner/version.json +1 -1
- {user_scanner-1.0.7.0.dist-info → user_scanner-1.0.8.1.dist-info}/METADATA +20 -14
- user_scanner-1.0.8.1.dist-info/RECORD +59 -0
- user_scanner-1.0.7.0.dist-info/RECORD +0 -55
- {user_scanner-1.0.7.0.dist-info → user_scanner-1.0.8.1.dist-info}/WHEEL +0 -0
- {user_scanner-1.0.7.0.dist-info → user_scanner-1.0.8.1.dist-info}/entry_points.txt +0 -0
- {user_scanner-1.0.7.0.dist-info → user_scanner-1.0.8.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# community
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
3
|
+
|
|
4
|
+
def validate_stackoverflow(user: str) -> Result:
|
|
5
|
+
url = f"https://stackoverflow.com/users/filter?search={user}"
|
|
6
|
+
|
|
7
|
+
def process(response):
|
|
8
|
+
if response.status_code == 200:
|
|
9
|
+
text = response.text
|
|
10
|
+
|
|
11
|
+
if "No users matched your search." in text:
|
|
12
|
+
return Result.available()
|
|
13
|
+
|
|
14
|
+
pattern = f'>{user}<'
|
|
15
|
+
if pattern in text:
|
|
16
|
+
return Result.taken()
|
|
17
|
+
|
|
18
|
+
return Result.available()
|
|
19
|
+
|
|
20
|
+
return Result.error("Unexpected status code from Stack Overflow")
|
|
21
|
+
|
|
22
|
+
return generic_validate(url, process)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
user = input("Username?: ").strip()
|
|
27
|
+
result = validate_stackoverflow(user)
|
|
28
|
+
|
|
29
|
+
if result == Result.available():
|
|
30
|
+
print("Available!")
|
|
31
|
+
elif result == Result.taken():
|
|
32
|
+
print("Unavailable!")
|
|
33
|
+
else:
|
|
34
|
+
msg = result.get_reason()
|
|
35
|
+
print("Error occurred!" + msg)
|
|
@@ -1,33 +1,19 @@
|
|
|
1
1
|
import importlib
|
|
2
|
-
import
|
|
2
|
+
import importlib.util
|
|
3
3
|
from colorama import Fore, Style
|
|
4
4
|
import threading
|
|
5
5
|
from itertools import permutations
|
|
6
6
|
import httpx
|
|
7
|
-
from httpx import ConnectError, TimeoutException
|
|
8
7
|
from pathlib import Path
|
|
9
|
-
from
|
|
8
|
+
from user_scanner.core.result import Result, AnyResult
|
|
9
|
+
from typing import Callable, Dict, List
|
|
10
10
|
|
|
11
11
|
lock = threading.Condition()
|
|
12
12
|
# Basically which thread is the one to print
|
|
13
13
|
print_queue = 0
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def load_categories() -> Dict[str, Path]:
|
|
17
|
-
root = Path(__file__).resolve().parent.parent # Should be user_scanner
|
|
18
|
-
categories = {}
|
|
19
|
-
|
|
20
|
-
for subfolder in root.iterdir():
|
|
21
|
-
if subfolder.is_dir() and \
|
|
22
|
-
not subfolder.name.lower() in ["cli", "utils", "core"] and \
|
|
23
|
-
not "__" in subfolder.name: # Removes __pycache__
|
|
24
|
-
categories[subfolder.name] = subfolder.resolve()
|
|
25
|
-
|
|
26
|
-
return categories
|
|
27
|
-
|
|
28
|
-
|
|
29
16
|
def load_modules(category_path: Path):
|
|
30
|
-
|
|
31
17
|
modules = []
|
|
32
18
|
for file in category_path.glob("*.py"):
|
|
33
19
|
if file.name == "__init__.py":
|
|
@@ -40,6 +26,19 @@ def load_modules(category_path: Path):
|
|
|
40
26
|
return modules
|
|
41
27
|
|
|
42
28
|
|
|
29
|
+
def load_categories() -> Dict[str, Path]:
|
|
30
|
+
root = Path(__file__).resolve().parent.parent # Should be user_scanner
|
|
31
|
+
categories = {}
|
|
32
|
+
|
|
33
|
+
for subfolder in root.iterdir():
|
|
34
|
+
if subfolder.is_dir() and \
|
|
35
|
+
not subfolder.name.lower() in ["cli", "utils", "core"] and \
|
|
36
|
+
not "__" in subfolder.name: # Removes __pycache__
|
|
37
|
+
categories[subfolder.name] = subfolder.resolve()
|
|
38
|
+
|
|
39
|
+
return categories
|
|
40
|
+
|
|
41
|
+
|
|
43
42
|
def worker_single(module, username, i):
|
|
44
43
|
global print_queue
|
|
45
44
|
|
|
@@ -53,12 +52,17 @@ def worker_single(module, username, i):
|
|
|
53
52
|
if func:
|
|
54
53
|
try:
|
|
55
54
|
result = func(username)
|
|
55
|
+
reason = ""
|
|
56
|
+
|
|
57
|
+
if isinstance(result, Result) and result.has_reason():
|
|
58
|
+
reason = f" ({result.get_reason()})"
|
|
59
|
+
|
|
56
60
|
if result == 1:
|
|
57
61
|
output = f" {Fore.GREEN}[✔] {site_name} ({username}): Available{Style.RESET_ALL}"
|
|
58
62
|
elif result == 0:
|
|
59
63
|
output = f" {Fore.RED}[✘] {site_name} ({username}): Taken{Style.RESET_ALL}"
|
|
60
64
|
else:
|
|
61
|
-
output = f" {Fore.YELLOW}[!] {site_name} ({username}): Error{Style.RESET_ALL}"
|
|
65
|
+
output = f" {Fore.YELLOW}[!] {site_name} ({username}): Error{reason}{Style.RESET_ALL}"
|
|
62
66
|
except Exception as e:
|
|
63
67
|
output = f" {Fore.YELLOW}[!] {site_name}: Exception - {e}{Style.RESET_ALL}"
|
|
64
68
|
else:
|
|
@@ -106,7 +110,7 @@ def run_checks(username):
|
|
|
106
110
|
print()
|
|
107
111
|
|
|
108
112
|
|
|
109
|
-
def make_get_request(url, **kwargs):
|
|
113
|
+
def make_get_request(url: str, **kwargs) -> httpx.Response:
|
|
110
114
|
"""Simple wrapper to **httpx.get** that predefines headers and timeout"""
|
|
111
115
|
if not "headers" in kwargs:
|
|
112
116
|
kwargs["headers"] = {
|
|
@@ -123,26 +127,24 @@ def make_get_request(url, **kwargs):
|
|
|
123
127
|
return httpx.get(url, **kwargs)
|
|
124
128
|
|
|
125
129
|
|
|
126
|
-
def generic_validate(url, func, **kwargs):
|
|
130
|
+
def generic_validate(url: str, func: Callable[[httpx.Response], AnyResult], **kwargs) -> AnyResult:
|
|
127
131
|
"""
|
|
128
132
|
A generic validate function that makes a request and executes the provided function on the response.
|
|
129
133
|
"""
|
|
130
134
|
try:
|
|
131
135
|
response = make_get_request(url, **kwargs)
|
|
132
136
|
return func(response)
|
|
133
|
-
except
|
|
134
|
-
return
|
|
135
|
-
except Exception:
|
|
136
|
-
return 2
|
|
137
|
+
except Exception as e:
|
|
138
|
+
return Result.error(e)
|
|
137
139
|
|
|
138
140
|
|
|
139
|
-
def status_validate(url, available, taken, **kwargs):
|
|
141
|
+
def status_validate(url: str, available: int | List[int], taken: int | List[int], **kwargs) -> Result:
|
|
140
142
|
"""
|
|
141
143
|
Function that takes a **url** and **kwargs** for the request and
|
|
142
144
|
checks if the request status matches the availabe or taken.
|
|
143
145
|
**Available** and **Taken** must either be whole numbers or lists of whole numbers.
|
|
144
146
|
"""
|
|
145
|
-
def inner(response):
|
|
147
|
+
def inner(response: httpx.Response):
|
|
146
148
|
# Checks if a number is equal or is contained inside
|
|
147
149
|
def contains(a, b): return (isinstance(a, list) and b in a) or (a == b)
|
|
148
150
|
status = response.status_code
|
|
@@ -150,12 +152,13 @@ def status_validate(url, available, taken, **kwargs):
|
|
|
150
152
|
taken_value = contains(taken, status)
|
|
151
153
|
|
|
152
154
|
if available_value and taken_value:
|
|
153
|
-
|
|
155
|
+
# Can't be both available and taken
|
|
156
|
+
return Result.error("Invalid status match. Report this on Github.")
|
|
154
157
|
elif available_value:
|
|
155
|
-
return
|
|
158
|
+
return Result.available()
|
|
156
159
|
elif taken_value:
|
|
157
|
-
return
|
|
158
|
-
return
|
|
160
|
+
return Result.taken()
|
|
161
|
+
return Result.error("Status didn't match. Report this on Github.")
|
|
159
162
|
|
|
160
163
|
return generic_validate(url, inner, **kwargs)
|
|
161
164
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def humanize_exception(e: Exception) -> str:
|
|
6
|
+
msg = str(e).lower()
|
|
7
|
+
|
|
8
|
+
if "10054" in msg:
|
|
9
|
+
return "Connection closed by remote server"
|
|
10
|
+
if "11001" in msg:
|
|
11
|
+
return "Could not resolve hostname"
|
|
12
|
+
|
|
13
|
+
return str(e)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Status(Enum):
|
|
17
|
+
TAKEN = 0
|
|
18
|
+
AVAILABLE = 1
|
|
19
|
+
ERROR = 2
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Result:
|
|
23
|
+
def __init__(self, status: Status, reason: str | Exception | None = None):
|
|
24
|
+
self.status = status
|
|
25
|
+
self.reason = reason
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def taken(cls):
|
|
29
|
+
return cls(Status.TAKEN)
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def available(cls):
|
|
33
|
+
return cls(Status.AVAILABLE)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def error(cls, reason: str | Exception | None = None):
|
|
37
|
+
return cls(Status.ERROR, reason)
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_number(cls, i: int, reason: str | Exception | None = None):
|
|
41
|
+
try:
|
|
42
|
+
status = Status(i)
|
|
43
|
+
except ValueError:
|
|
44
|
+
return cls(Status.ERROR, "Invalid status. Please contact maintainers.")
|
|
45
|
+
|
|
46
|
+
return cls(status, reason if status == Status.ERROR else None)
|
|
47
|
+
|
|
48
|
+
def to_number(self) -> int:
|
|
49
|
+
return self.status.value
|
|
50
|
+
|
|
51
|
+
def has_reason(self) -> bool:
|
|
52
|
+
return self.reason != None
|
|
53
|
+
|
|
54
|
+
def get_reason(self) -> str:
|
|
55
|
+
if self.reason == None:
|
|
56
|
+
return ""
|
|
57
|
+
if isinstance(self.reason, str):
|
|
58
|
+
return self.reason
|
|
59
|
+
#Format the exception
|
|
60
|
+
msg = humanize_exception(self.reason)
|
|
61
|
+
return f"{type(self.reason).__name__}: {msg.capitalize()}"
|
|
62
|
+
|
|
63
|
+
def __str__(self):
|
|
64
|
+
return self.get_reason()
|
|
65
|
+
|
|
66
|
+
def __eq__(self, other):
|
|
67
|
+
if isinstance(other, Status):
|
|
68
|
+
return self.status == other
|
|
69
|
+
|
|
70
|
+
if isinstance(other, Result):
|
|
71
|
+
return self.status == other.status
|
|
72
|
+
|
|
73
|
+
if isinstance(other, int):
|
|
74
|
+
return self.to_number() == other
|
|
75
|
+
|
|
76
|
+
return NotImplemented
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
AnyResult = Literal[0, 1, 2] | Result
|
user_scanner/creator/hashnode.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import httpx
|
|
2
|
-
import
|
|
3
|
-
from httpx import ConnectError, TimeoutException
|
|
2
|
+
from user_scanner.core.result import Result
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
def validate_hashnode(user):
|
|
@@ -27,21 +26,17 @@ def validate_hashnode(user):
|
|
|
27
26
|
|
|
28
27
|
if 'status' in data:
|
|
29
28
|
if data['status'] == 1:
|
|
30
|
-
return
|
|
29
|
+
return Result.available()
|
|
31
30
|
elif data['status'] == 0:
|
|
32
|
-
return
|
|
31
|
+
return Result.taken()
|
|
33
32
|
|
|
34
|
-
return
|
|
33
|
+
return Result.error("Status not found")
|
|
35
34
|
|
|
36
35
|
else:
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
except
|
|
40
|
-
return
|
|
41
|
-
except json.JSONDecodeError:
|
|
42
|
-
return 2
|
|
43
|
-
except Exception:
|
|
44
|
-
return 2
|
|
36
|
+
return Result.error("Invalid status code")
|
|
37
|
+
|
|
38
|
+
except Exception as e:
|
|
39
|
+
return Result.error(e)
|
|
45
40
|
|
|
46
41
|
|
|
47
42
|
if __name__ == "__main__":
|
user_scanner/creator/itch_io.py
CHANGED
|
@@ -2,11 +2,6 @@ from user_scanner.core.orchestrator import status_validate
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def validate_itch_io(user):
|
|
5
|
-
"""
|
|
6
|
-
Checks if a itch.io username is available.
|
|
7
|
-
Returns: 1 -> available, 0 -> taken, 2 -> error
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
5
|
url = f"https://{user}.itch.io"
|
|
11
6
|
|
|
12
7
|
return status_validate(url, 404, 200, follow_redirects=True)
|
user_scanner/creator/medium.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
from
|
|
1
|
+
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def validate_medium(user):
|
|
@@ -10,24 +10,19 @@ def validate_medium(user):
|
|
|
10
10
|
'Accept': "text/html",
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
response = httpx.get(url, headers=headers, timeout=3.0)
|
|
15
|
-
|
|
13
|
+
def process(response):
|
|
16
14
|
if response.status_code == 200:
|
|
17
15
|
html_text = response.text
|
|
18
16
|
|
|
19
17
|
username_tag = f'property="profile:username" content="{user}"'
|
|
20
18
|
|
|
21
19
|
if username_tag in html_text:
|
|
22
|
-
return
|
|
20
|
+
return Result.taken()
|
|
23
21
|
else:
|
|
24
|
-
return
|
|
25
|
-
return
|
|
22
|
+
return Result.available()
|
|
23
|
+
return Result.error()
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
return 2
|
|
29
|
-
except Exception:
|
|
30
|
-
return 2
|
|
25
|
+
return generic_validate(url, process, headers=headers)
|
|
31
26
|
|
|
32
27
|
|
|
33
28
|
if __name__ == "__main__":
|
user_scanner/creator/patreon.py
CHANGED
|
@@ -1,42 +1,47 @@
|
|
|
1
|
-
import
|
|
2
|
-
from httpx import ConnectError, TimeoutException
|
|
1
|
+
from user_scanner.core.orchestrator import status_validate, Result
|
|
3
2
|
|
|
4
3
|
|
|
5
|
-
def
|
|
6
|
-
url = f"https://
|
|
7
|
-
|
|
4
|
+
def validate_youtube(user) -> Result:
|
|
5
|
+
url = f"https://m.youtube.com/@{user}"
|
|
8
6
|
headers = {
|
|
9
|
-
'User-Agent': "Mozilla/5.0 (
|
|
7
|
+
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36",
|
|
10
8
|
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
11
|
-
'Accept-Encoding': "
|
|
12
|
-
'
|
|
9
|
+
'Accept-Encoding': "identity",
|
|
10
|
+
'sec-ch-dpr': "2.75",
|
|
11
|
+
'sec-ch-viewport-width': "980",
|
|
12
|
+
'sec-ch-ua': "\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"",
|
|
13
|
+
'sec-ch-ua-mobile': "?1",
|
|
14
|
+
'sec-ch-ua-full-version': "\"143.0.7499.52\"",
|
|
15
|
+
'sec-ch-ua-arch': "\"\"",
|
|
16
|
+
'sec-ch-ua-platform': "\"Android\"",
|
|
17
|
+
'sec-ch-ua-platform-version': "\"15.0.0\"",
|
|
18
|
+
'sec-ch-ua-bitness': "\"\"",
|
|
19
|
+
'sec-ch-ua-wow64': "?0",
|
|
20
|
+
'sec-ch-ua-full-version-list': "\"Google Chrome\";v=\"143.0.7499.52\", \"Chromium\";v=\"143.0.7499.52\", \"Not A(Brand\";v=\"24.0.0.0\"",
|
|
21
|
+
'sec-ch-ua-form-factors': "\"Mobile\"",
|
|
22
|
+
'upgrade-insecure-requests': "1",
|
|
23
|
+
'x-browser-channel': "stable",
|
|
24
|
+
'x-browser-year': "2025",
|
|
25
|
+
'x-browser-copyright': "Copyright 2025 Google LLC. All Rights reserved.",
|
|
26
|
+
'sec-fetch-site': "none",
|
|
27
|
+
'sec-fetch-mode': "navigate",
|
|
28
|
+
'sec-fetch-user': "?1",
|
|
29
|
+
'sec-fetch-dest': "document",
|
|
30
|
+
'accept-language': "en-US,en;q=0.9",
|
|
31
|
+
'priority': "u=0, i"
|
|
13
32
|
}
|
|
14
33
|
|
|
15
|
-
|
|
16
|
-
response = httpx.get(url, headers=headers,
|
|
17
|
-
timeout=3.0, follow_redirects=True)
|
|
18
|
-
status = response.status_code
|
|
19
|
-
|
|
20
|
-
if status == 200:
|
|
21
|
-
return 0
|
|
22
|
-
elif status == 404:
|
|
23
|
-
return 1
|
|
24
|
-
else:
|
|
25
|
-
return 2
|
|
26
|
-
|
|
27
|
-
except (ConnectError, TimeoutException):
|
|
28
|
-
return 2
|
|
29
|
-
except Exception:
|
|
30
|
-
return 2
|
|
34
|
+
return status_validate(url, 404, 200, headers=headers)
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
if __name__ == "__main__":
|
|
34
38
|
user = input("Username?: ").strip()
|
|
35
|
-
result =
|
|
39
|
+
result = validate_youtube(user)
|
|
36
40
|
|
|
37
41
|
if result == 1:
|
|
38
42
|
print("Available!")
|
|
39
43
|
elif result == 0:
|
|
40
44
|
print("Unavailable!")
|
|
41
45
|
else:
|
|
42
|
-
|
|
46
|
+
reason = result.get_reason()
|
|
47
|
+
print(f"Error occurred! Reason: {reason}")
|
user_scanner/dev/github.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from user_scanner.core.orchestrator import
|
|
1
|
+
from user_scanner.core.orchestrator import generic_validate, Result
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def validate_github(user):
|
|
@@ -18,7 +18,24 @@ def validate_github(user):
|
|
|
18
18
|
'priority': "u=1, i"
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
GITHUB_INVALID_MSG = (
|
|
22
|
+
"Username may only contain alphanumeric characters or single hyphens, "
|
|
23
|
+
"and cannot begin or end with a hyphen."
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def process(response):
|
|
27
|
+
if response.status_code == 200:
|
|
28
|
+
return Result.available()
|
|
29
|
+
|
|
30
|
+
if response.status_code == 422:
|
|
31
|
+
if GITHUB_INVALID_MSG in response.text:
|
|
32
|
+
return Result.error("Cannot start/end with hyphen or use double hyphens")
|
|
33
|
+
|
|
34
|
+
return Result.taken()
|
|
35
|
+
|
|
36
|
+
return Result.error("Unexpected GitHub response report it via issues")
|
|
37
|
+
|
|
38
|
+
return generic_validate(url, process, headers=headers)
|
|
22
39
|
|
|
23
40
|
|
|
24
41
|
if __name__ == "__main__":
|
user_scanner/dev/gitlab.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def validate_gitlab(user):
|
|
@@ -18,11 +19,11 @@ def validate_gitlab(user):
|
|
|
18
19
|
# Corrected: Compare against Python boolean True/False
|
|
19
20
|
# AVAILABLE (return 1) if "exists": true
|
|
20
21
|
if data['exists'] is False:
|
|
21
|
-
return
|
|
22
|
+
return Result.available()
|
|
22
23
|
# UNAVAILABLE (return 0) if "exists": false
|
|
23
24
|
elif data['exists'] is True:
|
|
24
|
-
return
|
|
25
|
-
return
|
|
25
|
+
return Result.taken()
|
|
26
|
+
return Result.error("Invalid status code")
|
|
26
27
|
|
|
27
28
|
return generic_validate(url, process, headers=headers)
|
|
28
29
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from user_scanner.core.orchestrator import status_validate
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def validate_huggingface(user):
|
|
5
|
+
url = f"https://huggingface.co/{user}"
|
|
6
|
+
|
|
7
|
+
return status_validate(url, 404, 200, follow_redirects=True)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
user = input("Username?: ").strip()
|
|
12
|
+
result = validate_huggingface(user)
|
|
13
|
+
|
|
14
|
+
if result == 1:
|
|
15
|
+
print("Available!")
|
|
16
|
+
elif result == 0:
|
|
17
|
+
print("Unavailable!")
|
|
18
|
+
else:
|
|
19
|
+
print("Error occurred!")
|
user_scanner/dev/npmjs.py
CHANGED
|
@@ -1,34 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
from
|
|
1
|
+
import re
|
|
2
|
+
from user_scanner.core.orchestrator import status_validate, Result
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def validate_npmjs(user):
|
|
6
|
-
|
|
6
|
+
if re.match(r'^[^a-zA-Z0-9_-]', user):
|
|
7
|
+
return Result.error("Username cannot start with a period")
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
11
|
-
'Accept-Encoding': "gzip, deflate, br, zstd",
|
|
12
|
-
'accept-language': "en-US,en;q=0.9",
|
|
13
|
-
'priority': "u=0, i"
|
|
14
|
-
}
|
|
9
|
+
if re.search(r'[A-Z]', user):
|
|
10
|
+
return Result.error("Username cannot contain uppercase letters.")
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
response = httpx.head(url, headers=headers,
|
|
18
|
-
timeout=3.0, follow_redirects=True)
|
|
19
|
-
status = response.status_code
|
|
12
|
+
url = f"https://www.npmjs.com/~{user}"
|
|
20
13
|
|
|
21
|
-
if status == 200:
|
|
22
|
-
return 0
|
|
23
|
-
elif status == 404:
|
|
24
|
-
return 1
|
|
25
|
-
else:
|
|
26
|
-
return 2
|
|
27
14
|
|
|
28
|
-
|
|
29
|
-
return 2
|
|
30
|
-
except Exception:
|
|
31
|
-
return 2
|
|
15
|
+
return status_validate(url, 404, 200, timeout=3.0, follow_redirects=True)
|
|
32
16
|
|
|
33
17
|
|
|
34
18
|
if __name__ == "__main__":
|
|
@@ -40,4 +24,4 @@ if __name__ == "__main__":
|
|
|
40
24
|
elif result == 0:
|
|
41
25
|
print("Unavailable!")
|
|
42
26
|
else:
|
|
43
|
-
print("Error occurred!")
|
|
27
|
+
print(f"Error occurred! Reason: {result}")
|
user_scanner/gaming/chess_com.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def validate_chess_com(user):
|
|
@@ -16,22 +17,16 @@ def validate_chess_com(user):
|
|
|
16
17
|
data = response.json()
|
|
17
18
|
if data.get('valid') is True:
|
|
18
19
|
# 'valid': true means the username is NOT taken
|
|
19
|
-
return
|
|
20
|
+
return Result.available()
|
|
20
21
|
elif data.get('valid') is False:
|
|
21
22
|
# 'valid': false means the username IS taken
|
|
22
|
-
return
|
|
23
|
-
return
|
|
23
|
+
return Result.taken()
|
|
24
|
+
return Result.error("Invalid status code")
|
|
24
25
|
|
|
25
26
|
return generic_validate(url, process, headers=headers)
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
if __name__ == "__main__":
|
|
29
|
-
try:
|
|
30
|
-
import httpx
|
|
31
|
-
except ImportError:
|
|
32
|
-
print("Error: 'httpx' library is not installed.")
|
|
33
|
-
exit()
|
|
34
|
-
|
|
35
30
|
user = input("Username?: ").strip()
|
|
36
31
|
result = validate_chess_com(user)
|
|
37
32
|
|
user_scanner/gaming/minecraft.py
CHANGED
|
@@ -2,11 +2,6 @@ from user_scanner.core.orchestrator import status_validate
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def validate_minecraft(user):
|
|
5
|
-
"""
|
|
6
|
-
Checks for minecraft username with mojang api.
|
|
7
|
-
Returns: 1 -> available, 0 -> taken, 2 -> error
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
5
|
url = f"https://api.mojang.com/minecraft/profile/lookup/name/{user}"
|
|
11
6
|
|
|
12
7
|
return status_validate(url, 404, 200, follow_redirects=True)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
-
|
|
2
|
+
from user_scanner.core.result import Result
|
|
3
3
|
|
|
4
4
|
def validate_monkeytype(user: str) -> int:
|
|
5
5
|
|
|
@@ -25,21 +25,15 @@ def validate_monkeytype(user: str) -> int:
|
|
|
25
25
|
available = payload.get("available")
|
|
26
26
|
|
|
27
27
|
if available is True:
|
|
28
|
-
return
|
|
28
|
+
return Result.available()
|
|
29
29
|
elif available is False:
|
|
30
|
-
return
|
|
31
|
-
return
|
|
30
|
+
return Result.taken()
|
|
31
|
+
return Result.error("Invalid status code")
|
|
32
32
|
|
|
33
33
|
return generic_validate(url, process, headers=headers)
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
if __name__ == "__main__":
|
|
37
|
-
try:
|
|
38
|
-
import httpx # noqa: F401
|
|
39
|
-
except ImportError:
|
|
40
|
-
print("Error: 'httpx' library is not installed.")
|
|
41
|
-
raise SystemExit(1)
|
|
42
|
-
|
|
43
37
|
user = input("Username?: ").strip()
|
|
44
38
|
result = validate_monkeytype(user)
|
|
45
39
|
|
user_scanner/gaming/osu.py
CHANGED
|
@@ -2,11 +2,6 @@ from user_scanner.core.orchestrator import status_validate
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def validate_osu(user):
|
|
5
|
-
"""
|
|
6
|
-
Checks if a Osu username is available.
|
|
7
|
-
Returns: 1 -> available, 0 -> taken, 2 -> error
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
5
|
url = f"https://osu.ppy.sh/users/{user}"
|
|
11
6
|
|
|
12
7
|
return status_validate(url, 404, [200, 302], follow_redirects=True)
|
user_scanner/gaming/roblox.py
CHANGED
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def validate_roblox(user):
|
|
5
|
-
"""
|
|
6
|
-
Checks if a roblox username is available.
|
|
7
|
-
Returns: 1 -> available, 0 -> taken, 2 -> error
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
6
|
# official api
|
|
11
7
|
url = f"https://users.roblox.com/v1/users/search?keyword={user}&limit=10"
|
|
12
8
|
|
|
13
9
|
def process(response):
|
|
14
10
|
search_results = response.json() # api response
|
|
15
11
|
|
|
16
|
-
if
|
|
17
|
-
return
|
|
12
|
+
if response.status_code == 429:
|
|
13
|
+
return Result.error("Too many requests")
|
|
14
|
+
|
|
15
|
+
if response.status_code == 400:
|
|
16
|
+
error = search_results["errors"][0] #Api states theres always an error
|
|
17
|
+
if error["code"] == 6:
|
|
18
|
+
return Result.error("Username is too short")
|
|
19
|
+
if error["code"] == 5:
|
|
20
|
+
return Result.error("Username was filtered")
|
|
21
|
+
#Shouldn't be able to reach this
|
|
22
|
+
return Result.error("Invalid username")
|
|
18
23
|
|
|
19
24
|
# iterates through the entries in the search results
|
|
20
25
|
for entry in search_results["data"]:
|
|
21
26
|
# .lower() so casing from the API doesn't matter
|
|
22
27
|
if entry["name"].lower() == user.lower(): # if a username matches the user
|
|
23
|
-
return
|
|
24
|
-
return
|
|
28
|
+
return Result.taken()
|
|
29
|
+
return Result.available()
|
|
25
30
|
|
|
26
31
|
return generic_validate(url, process, follow_redirects=True)
|
|
27
32
|
|
user_scanner/gaming/steam.py
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def validate_steam(user):
|
|
5
|
-
"""
|
|
6
|
-
Checks if a steam username is available.
|
|
7
|
-
Returns: 1 -> available, 0 -> taken, 2 -> error
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
6
|
url = f"https://steamcommunity.com/id/{user}/"
|
|
11
7
|
|
|
12
8
|
def process(response):
|
|
13
9
|
if response.status_code == 200:
|
|
14
|
-
if
|
|
15
|
-
return
|
|
10
|
+
if "Error</title>" in response.text:
|
|
11
|
+
return Result.available()
|
|
16
12
|
else:
|
|
17
|
-
return
|
|
18
|
-
|
|
13
|
+
return Result.taken()
|
|
14
|
+
|
|
15
|
+
return Result.error("Invalid status code")
|
|
19
16
|
|
|
20
17
|
return generic_validate(url, process)
|
|
21
18
|
|
user_scanner/social/bluesky.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import re
|
|
2
1
|
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def validate_bluesky(user):
|
|
@@ -25,30 +25,24 @@ def validate_bluesky(user):
|
|
|
25
25
|
'handle': handle,
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
if not re.fullmatch(r"^[a-zA-Z0-9\.-]{1,64}$", user):
|
|
29
|
-
return 2
|
|
30
|
-
|
|
31
28
|
def process(response):
|
|
32
29
|
if response.status_code == 200:
|
|
33
30
|
data = response.json()
|
|
34
31
|
result_type = data.get('result', {}).get('$type')
|
|
35
32
|
|
|
36
33
|
if result_type == "com.atproto.temp.checkHandleAvailability#resultAvailable":
|
|
37
|
-
return
|
|
34
|
+
return Result.available()
|
|
38
35
|
elif result_type == "com.atproto.temp.checkHandleAvailability#resultUnavailable":
|
|
39
|
-
return
|
|
40
|
-
|
|
36
|
+
return Result.taken()
|
|
37
|
+
elif response.status_code == 400:
|
|
38
|
+
return Result.error("Username can only contain letters, numbers, hyphens (no leading/trailing)")
|
|
39
|
+
|
|
40
|
+
return Result.error("Invalid status code!")
|
|
41
41
|
|
|
42
42
|
return generic_validate(url, process, headers=headers, params=params, timeout=15.0)
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
if __name__ == "__main__":
|
|
46
|
-
try:
|
|
47
|
-
import httpx
|
|
48
|
-
except ImportError:
|
|
49
|
-
print("Error: 'httpx' library is not installed.")
|
|
50
|
-
exit()
|
|
51
|
-
|
|
52
46
|
user = input("Username?: ").strip()
|
|
53
47
|
result = validate_bluesky(user)
|
|
54
48
|
|
user_scanner/social/discord.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import httpx
|
|
2
|
-
from
|
|
3
|
-
|
|
2
|
+
from user_scanner.core.result import Result
|
|
4
3
|
|
|
5
4
|
def validate_discord(user):
|
|
6
5
|
url = "https://discord.com/api/v9/unique-username/username-attempt-unauthed"
|
|
@@ -21,14 +20,12 @@ def validate_discord(user):
|
|
|
21
20
|
if response.status_code == 200:
|
|
22
21
|
status = response.json().get("taken")
|
|
23
22
|
if status is True:
|
|
24
|
-
return
|
|
23
|
+
return Result.taken()
|
|
25
24
|
elif status is False:
|
|
26
|
-
return
|
|
27
|
-
return
|
|
28
|
-
except
|
|
29
|
-
return
|
|
30
|
-
except Exception:
|
|
31
|
-
return 2
|
|
25
|
+
return Result.available()
|
|
26
|
+
return Result.error("Invalid status code")
|
|
27
|
+
except Exception as e:
|
|
28
|
+
return Result.error(e)
|
|
32
29
|
|
|
33
30
|
|
|
34
31
|
if __name__ == "__main__":
|
user_scanner/social/mastodon.py
CHANGED
user_scanner/social/pinterest.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
-
|
|
2
|
+
from user_scanner.core.result import Result
|
|
3
3
|
|
|
4
4
|
def validate_pinterest(user):
|
|
5
5
|
url = f"https://www.pinterest.com/{user}/"
|
|
@@ -7,11 +7,11 @@ def validate_pinterest(user):
|
|
|
7
7
|
def process(response):
|
|
8
8
|
if response.status_code == 200:
|
|
9
9
|
if "User not found." in response.text:
|
|
10
|
-
return
|
|
10
|
+
return Result.available()
|
|
11
11
|
else:
|
|
12
|
-
return
|
|
12
|
+
return Result.taken()
|
|
13
13
|
else:
|
|
14
|
-
return
|
|
14
|
+
return Result.error("Invalid status code")
|
|
15
15
|
|
|
16
16
|
return generic_validate(url, process, follow_redirects=True)
|
|
17
17
|
|
user_scanner/social/reddit.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
def validate_reddit(user):
|
|
@@ -7,11 +8,11 @@ def validate_reddit(user):
|
|
|
7
8
|
def process(response):
|
|
8
9
|
if response.status_code == 200:
|
|
9
10
|
if "Sorry, nobody on Reddit goes by that name." in response.text:
|
|
10
|
-
return
|
|
11
|
+
return Result.available()
|
|
11
12
|
else:
|
|
12
|
-
return
|
|
13
|
+
return Result.taken()
|
|
13
14
|
else:
|
|
14
|
-
return
|
|
15
|
+
return Result.error()
|
|
15
16
|
|
|
16
17
|
return generic_validate(url, process, follow_redirects=True)
|
|
17
18
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from user_scanner.core.orchestrator import generic_validate
|
|
2
|
+
from user_scanner.core.result import Result
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def validate_soundcloud(user):
|
|
6
|
+
url = f"https://soundcloud.com/{user}"
|
|
7
|
+
|
|
8
|
+
headers = {
|
|
9
|
+
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
10
|
+
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def process(response):
|
|
14
|
+
if response.status_code == 404:
|
|
15
|
+
return Result.available()
|
|
16
|
+
|
|
17
|
+
if response.status_code == 200:
|
|
18
|
+
text = response.text
|
|
19
|
+
|
|
20
|
+
if f'soundcloud://users:{user}' in text:
|
|
21
|
+
return Result.taken()
|
|
22
|
+
if f'"username":"{user}"' in text:
|
|
23
|
+
return Result.taken()
|
|
24
|
+
if 'soundcloud://users:' in text and '"username":"' in text:
|
|
25
|
+
return Result.taken()
|
|
26
|
+
|
|
27
|
+
return Result.available()
|
|
28
|
+
|
|
29
|
+
return Result.error()
|
|
30
|
+
|
|
31
|
+
return generic_validate(url, process, headers=headers, follow_redirects=True)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
user = input("Username?: ").strip()
|
|
36
|
+
result = validate_soundcloud(user)
|
|
37
|
+
|
|
38
|
+
if result == 1:
|
|
39
|
+
print("Available!")
|
|
40
|
+
elif result == 0:
|
|
41
|
+
print("Unavailable!")
|
|
42
|
+
else:
|
|
43
|
+
print("Error occured!")
|
user_scanner/social/telegram.py
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from user_scanner.core.orchestrator import generic_validate
|
|
3
|
+
from user_scanner.core.result import Result
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
def validate_telegram(user: str) -> int:
|
|
6
|
-
"""
|
|
7
|
-
Checks if a Telegram username is available.
|
|
8
|
-
Returns: 1 -> available, 0 -> taken, 2 -> error
|
|
9
|
-
"""
|
|
10
7
|
url = f"https://t.me/{user}"
|
|
11
8
|
|
|
12
9
|
def process(r):
|
|
13
10
|
if r.status_code == 200:
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
if re.search(r'<div[^>]*class="tgme_page_extra"[^>]*>', r.text):
|
|
12
|
+
return Result.taken()
|
|
13
|
+
else:
|
|
14
|
+
return Result.available()
|
|
15
|
+
return Result.error()
|
|
16
16
|
|
|
17
17
|
return generic_validate(url, process, follow_redirects=True)
|
|
18
18
|
|
user_scanner/social/x.py
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
from colorama import Fore, Style
|
|
4
|
-
from httpx import ConnectError, TimeoutException
|
|
5
|
-
|
|
1
|
+
from user_scanner.core.result import Result
|
|
2
|
+
from user_scanner.core.orchestrator import generic_validate
|
|
6
3
|
|
|
7
4
|
def validate_x(user):
|
|
8
5
|
url = "https://api.twitter.com/i/users/username_available.json"
|
|
@@ -18,31 +15,24 @@ def validate_x(user):
|
|
|
18
15
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
|
|
19
16
|
}
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
response = httpx.get(url, params=params, headers=headers, timeout=3.0)
|
|
18
|
+
def process(response):
|
|
23
19
|
status = response.status_code
|
|
24
|
-
|
|
20
|
+
|
|
25
21
|
if status in [401, 403, 429]:
|
|
26
|
-
return
|
|
22
|
+
return Result.error()
|
|
27
23
|
|
|
28
24
|
elif status == 200:
|
|
29
25
|
data = response.json()
|
|
30
26
|
if data.get('valid') is True:
|
|
31
|
-
return
|
|
27
|
+
return Result.available()
|
|
32
28
|
elif data.get('reason') == 'taken':
|
|
33
|
-
return
|
|
29
|
+
return Result.taken()
|
|
34
30
|
elif (data.get('reason') == "improper_format" or data.get('reason') == "invalid_username"):
|
|
35
|
-
|
|
36
|
-
"\n" + " "+f"{Fore.CYAN}X says: {data.get('desc')}{Style.RESET_ALL}")
|
|
37
|
-
return 2
|
|
38
|
-
else:
|
|
39
|
-
return 2
|
|
31
|
+
return Result.error(f"X says: {data.get('desc')}")
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
return 2
|
|
43
|
-
except Exception:
|
|
44
|
-
return 2
|
|
33
|
+
return Result.error()
|
|
45
34
|
|
|
35
|
+
return generic_validate(url, process, params=params, headers=headers)
|
|
46
36
|
|
|
47
37
|
if __name__ == "__main__":
|
|
48
38
|
user = input("Username?: ").strip()
|
user_scanner/social/youtube.py
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
from httpx import ConnectError, TimeoutException
|
|
1
|
+
from user_scanner.core.orchestrator import status_validate, Result
|
|
3
2
|
|
|
4
3
|
|
|
5
|
-
def validate_youtube(user):
|
|
4
|
+
def validate_youtube(user) -> Result:
|
|
6
5
|
url = f"https://m.youtube.com/@{user}"
|
|
7
|
-
|
|
8
6
|
headers = {
|
|
9
|
-
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
7
|
+
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36",
|
|
10
8
|
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
11
|
-
'Accept-Encoding': "
|
|
12
|
-
'
|
|
13
|
-
'sec-ch-
|
|
9
|
+
'Accept-Encoding': "identity",
|
|
10
|
+
'sec-ch-dpr': "2.75",
|
|
11
|
+
'sec-ch-viewport-width': "980",
|
|
12
|
+
'sec-ch-ua': "\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"",
|
|
14
13
|
'sec-ch-ua-mobile': "?1",
|
|
15
|
-
'sec-ch-ua-full-version': "\"
|
|
14
|
+
'sec-ch-ua-full-version': "\"143.0.7499.52\"",
|
|
16
15
|
'sec-ch-ua-arch': "\"\"",
|
|
17
16
|
'sec-ch-ua-platform': "\"Android\"",
|
|
18
17
|
'sec-ch-ua-platform-version': "\"15.0.0\"",
|
|
18
|
+
'sec-ch-ua-model': "\"I2404\"",
|
|
19
19
|
'sec-ch-ua-bitness': "\"\"",
|
|
20
20
|
'sec-ch-ua-wow64': "?0",
|
|
21
|
-
'sec-ch-ua-full-version-list': "\"Google Chrome\";v=\"
|
|
21
|
+
'sec-ch-ua-full-version-list': "\"Google Chrome\";v=\"143.0.7499.52\", \"Chromium\";v=\"143.0.7499.52\", \"Not A(Brand\";v=\"24.0.0.0\"",
|
|
22
22
|
'sec-ch-ua-form-factors': "\"Mobile\"",
|
|
23
23
|
'upgrade-insecure-requests': "1",
|
|
24
|
+
'x-browser-channel': "stable",
|
|
25
|
+
'x-browser-year': "2025",
|
|
26
|
+
'x-browser-copyright': "Copyright 2025 Google LLC. All Rights reserved.",
|
|
24
27
|
'sec-fetch-site': "none",
|
|
25
28
|
'sec-fetch-mode': "navigate",
|
|
26
29
|
'sec-fetch-user': "?1",
|
|
@@ -29,22 +32,8 @@ def validate_youtube(user):
|
|
|
29
32
|
'priority': "u=0, i"
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
try:
|
|
33
|
-
response = httpx.get(url, headers=headers,
|
|
34
|
-
follow_redirects=True, timeout=3.0)
|
|
35
|
-
status = response.status_code
|
|
36
|
-
|
|
37
|
-
if status == 200:
|
|
38
|
-
return 0
|
|
39
|
-
elif status == 404:
|
|
40
|
-
return 1
|
|
41
|
-
else:
|
|
42
|
-
return 2
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
return 2
|
|
46
|
-
except Exception:
|
|
47
|
-
return 2
|
|
36
|
+
return status_validate(url, 404, 200, headers=headers)
|
|
48
37
|
|
|
49
38
|
|
|
50
39
|
if __name__ == "__main__":
|
|
@@ -56,4 +45,6 @@ if __name__ == "__main__":
|
|
|
56
45
|
elif result == 0:
|
|
57
46
|
print("Unavailable!")
|
|
58
47
|
else:
|
|
59
|
-
|
|
48
|
+
reason = result.get_reason()
|
|
49
|
+
print(f"Error occurred! Reason: {reason}")
|
|
50
|
+
|
user_scanner/version.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: user-scanner
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8.1
|
|
4
4
|
Summary: Check username availability across multiple popular platforms
|
|
5
5
|
Keywords: username,checker,availability,social,tech,python,user-scanner
|
|
6
6
|
Author-email: Kaif <kafcodec@gmail.com>
|
|
@@ -15,7 +15,7 @@ Project-URL: Homepage, https://github.com/kaifcodec/user-scanner
|
|
|
15
15
|
|
|
16
16
|

|
|
17
17
|
<p align="center">
|
|
18
|
-
<img src="https://img.shields.io/badge/Version-1.0.
|
|
18
|
+
<img src="https://img.shields.io/badge/Version-1.0.8.1-blueviolet?style=for-the-badge&logo=github" />
|
|
19
19
|
<img src="https://img.shields.io/github/issues/kaifcodec/user-scanner?style=for-the-badge&logo=github" />
|
|
20
20
|
<img src="https://img.shields.io/badge/Tested%20on-Termux-black?style=for-the-badge&logo=termux" />
|
|
21
21
|
<img src="https://img.shields.io/badge/Tested%20on-Windows-cyan?style=for-the-badge&logo=Windows" />
|
|
@@ -33,9 +33,10 @@ Perfect for finding a **unique username** across GitHub, Twitter, Reddit, Instag
|
|
|
33
33
|
|
|
34
34
|
- ✅ Check usernames across **social networks**, **developer platforms**, and **creator communities**.
|
|
35
35
|
- ✅ Clear **Available / Taken / Error** output for each platform.
|
|
36
|
+
- ✅ Robust error handling: It prints the exact reason (e.g. Cannot use underscores, hyphens at the start/end)
|
|
36
37
|
- ✅ Fully modular: add new platform modules easily.
|
|
37
|
-
- ✅ Wildcard-based username permutations for automatic variation generation
|
|
38
|
-
- ✅ Command-line interface ready: works directly after `pip install
|
|
38
|
+
- ✅ Wildcard-based username permutations for automatic variation generation using provided suffix
|
|
39
|
+
- ✅ Command-line interface ready: works directly after `pip install`
|
|
39
40
|
- ✅ Can be used as username OSINT tool.
|
|
40
41
|
- ✅ Very low and lightweight dependencies, can be run on any machine.
|
|
41
42
|
---
|
|
@@ -85,7 +86,13 @@ user-scanner -u <username> -p <suffix> -d <seconds> #delay to avoid rate-limits
|
|
|
85
86
|
|
|
86
87
|
- Note*: New modules are constantly getting added so this might have only limited, outdated output:
|
|
87
88
|
|
|
88
|
-
|
|
89
|
+
|
|
90
|
+
<img width="1080" height="770" alt="1000140392" src="https://github.com/user-attachments/assets/4638c8f6-40c6-46f8-ae17-ac65cd199d81" />
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
<img width="1080" height="352" alt="1000140393" src="https://github.com/user-attachments/assets/578b248c-2a05-4917-aab3-6372a7c28045" />
|
|
89
96
|
|
|
90
97
|
|
|
91
98
|
### Contributing:
|
|
@@ -103,18 +110,17 @@ user_scanner/
|
|
|
103
110
|
```
|
|
104
111
|
|
|
105
112
|
**Module guidelines:**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
113
|
+
This project contains small "validator" modules that check whether a username exists on a given platform. Each validator is a single function that returns a Result object (see `core/orchestrator.py`).
|
|
114
|
+
|
|
115
|
+
Result semantics:
|
|
116
|
+
- Result.available() → `available`
|
|
117
|
+
- Result.taken() → `taken`
|
|
118
|
+
- Result.error(message: Optional[str]) → `error`, blocked, unknown, or request failure (include short diagnostic message when helpful)
|
|
119
|
+
|
|
120
|
+
Follow this document when adding or updating validators.
|
|
112
121
|
|
|
113
122
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for examples.
|
|
114
123
|
|
|
115
|
-
### 📧 Contact:
|
|
116
|
-
- [Email](kaifcodec@gmail.com)
|
|
117
|
-
|
|
118
124
|
---
|
|
119
125
|
|
|
120
126
|
### Dependencies:
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
user_scanner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
user_scanner/__main__.py,sha256=r_8gq8wSRs3U7yUkTAv4e4MPopX0MUf_EbwedBTPs44,5082
|
|
3
|
+
user_scanner/version.json,sha256=_1eU-X0UN6AjXfkZNFG3CCPFc6S2N4WQmSrbnp5Dvog,49
|
|
4
|
+
user_scanner/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
user_scanner/cli/banner.py,sha256=3t6owaDArERlvpcszA1Yi3dtksvh8a9tLyrxRowTC40,1499
|
|
6
|
+
user_scanner/community/__init__.py,sha256=5EzlM991pJqvqIRc05_QV5BureJZ7wiCRm1AyEY6pms,12
|
|
7
|
+
user_scanner/community/coderlegion.py,sha256=W_bdjzdFPRgUrNFFlylvToSJ4AzaFCtTsUy_MRVDdSo,451
|
|
8
|
+
user_scanner/community/stackoverflow.py,sha256=MTL8O0TLHkjVbugBh1pLxELJLU3hkX_YEHjGjaKTJi4,1007
|
|
9
|
+
user_scanner/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
user_scanner/core/orchestrator.py,sha256=8dLzV-0zUafZxMLIGAT65Tp4YJtvboelR6eL9M22_4M,6124
|
|
11
|
+
user_scanner/core/result.py,sha256=HWz9JjRk74shAdM56ZtdG6hIp4dgcahqYSnpOp3uCak,1933
|
|
12
|
+
user_scanner/creator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
user_scanner/creator/devto.py,sha256=mIACmG1a4eoctywxb5p04sI0YVi3dsjCRw9YVOFBEKQ,435
|
|
14
|
+
user_scanner/creator/hashnode.py,sha256=vlXsJfIG_5ShEK2xPLDl2bi6X-d08iTeoqfKeQLaB1g,1363
|
|
15
|
+
user_scanner/creator/itch_io.py,sha256=JsFkFzBfJI18DeuSYJIOPGEV_9v7t-jtwmBYCA9W2P8,440
|
|
16
|
+
user_scanner/creator/kaggle.py,sha256=QaXIG02OGxvQZEvwHm50RKNd7joxGOq0Ht3cFfrYEiU,445
|
|
17
|
+
user_scanner/creator/medium.py,sha256=NIOYnk8_ASD0kYfKqs8t6uZZTV4D-5-ZxyHMzOMMOuI,1015
|
|
18
|
+
user_scanner/creator/patreon.py,sha256=g-r85pxirf0ihK3STyGYPIzp59MB7JH64Opb4wq1fyU,461
|
|
19
|
+
user_scanner/creator/producthunt.py,sha256=p0HoIIVhmv9bBkelhfzRYudUFoyk_qeT66-hPpHEFqk,1938
|
|
20
|
+
user_scanner/dev/__init__.py,sha256=qUR0eLwN-gO6oKk-1cmCVT4G_AxUHHMgpV3wJ7URXi4,7
|
|
21
|
+
user_scanner/dev/codeberg.py,sha256=Z6nV0_8xZhMiCcNn9Hn79VVh6y0ar9fqL7KS2b7IaDo,447
|
|
22
|
+
user_scanner/dev/cratesio.py,sha256=mJnlLJoMLlQ7f_95QD7LgH1xCj-e6FooOFkpYypBfG4,724
|
|
23
|
+
user_scanner/dev/dockerhub.py,sha256=sPEnomGiPM2mKv2HsA-9WxaXHjzz21A6ox3IXK1etLc,643
|
|
24
|
+
user_scanner/dev/github.py,sha256=km0RMd4cS5sY8IUKDKoNC1oQeCj57ld4HAjOlo1w4ms,1689
|
|
25
|
+
user_scanner/dev/gitlab.py,sha256=kMDSd74XbofmJocfS4Fd9DxPryIHBMek3N_5c7Z_AJQ,1351
|
|
26
|
+
user_scanner/dev/huggingface.py,sha256=hDanOZ45LeUg3hrN0CYrBnBnLqHCYtOWS0_HCvAbmDw,454
|
|
27
|
+
user_scanner/dev/launchpad.py,sha256=N58ioX_dEHq2uwyyGrWnDKWwbqK9_RiuBQ1uWR5cDfg,799
|
|
28
|
+
user_scanner/dev/npmjs.py,sha256=k-DhFqGJWDoQ79EzR8hmVrJk07AfJfPUWnIYuKc2G6w,713
|
|
29
|
+
user_scanner/dev/replit.py,sha256=SI_i2l4w9tm2kBX4-cONBAT8dSynXoGEP4zcU8ngnh0,442
|
|
30
|
+
user_scanner/donation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
user_scanner/donation/buymeacoffee.py,sha256=86LGyChv_UKQFp2D7nIoK1B-FCAAbbfabS8NA9yLp5k,459
|
|
32
|
+
user_scanner/donation/liberapay.py,sha256=njClxpbRLZQ_L2-lUYCY6QFnF4IcwfCJPCIg1iEqo7M,1120
|
|
33
|
+
user_scanner/gaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
user_scanner/gaming/chess_com.py,sha256=74tMgukSUXwdmD9G7Jij_gudRlSfs46Xho5KNMVeyt4,1262
|
|
35
|
+
user_scanner/gaming/minecraft.py,sha256=7a9H9ebLlRzGB0SjxLmzqLiDPDBZAuuNq3KKe2DZAvo,481
|
|
36
|
+
user_scanner/gaming/monkeytype.py,sha256=n9KMBChs0ej7MgZqLGUDVz5CED70sQ3ksDF5pO0G05A,1380
|
|
37
|
+
user_scanner/gaming/osu.py,sha256=2Xs1iM0CJ-3dNHu4tyF50_s0Ei_1mA5Zd6D6M5RmiVg,448
|
|
38
|
+
user_scanner/gaming/roblox.py,sha256=Qs51jLgKh-Ehqlco_j8CFtJ4CLVoZeBwPugDvAyLw3Q,1464
|
|
39
|
+
user_scanner/gaming/steam.py,sha256=l8xk_p9aiYQWCPoogQnO1iwkfojPhg6yd76OZHhKN50,740
|
|
40
|
+
user_scanner/social/__init__.py,sha256=jaCkFwX1uYtF0ENifVwF8OfHrYYUTm64B9wlBq9BBfQ,9
|
|
41
|
+
user_scanner/social/bluesky.py,sha256=11Y_vRj3txEDQqoD0iANgSWVSB8L87OotPQZquhneR0,1994
|
|
42
|
+
user_scanner/social/discord.py,sha256=z-oIqT416ydnZUkq481rz6NTq5yc_BYu-P_Z79uR-Jw,1150
|
|
43
|
+
user_scanner/social/instagram.py,sha256=GgmKGvi3meKdZ_nQJbJSBZDJTEKSoE6Cn4_VARmo62I,953
|
|
44
|
+
user_scanner/social/mastodon.py,sha256=qISx-gUsddC8lFMcmERA4N0YAnXyS1Jq2Xgg7XE4sL4,450
|
|
45
|
+
user_scanner/social/pinterest.py,sha256=JIJ-HPtMoGvxW7NQzm02lChFKMmE6k6GxFoUZ6OvCec,784
|
|
46
|
+
user_scanner/social/reddit.py,sha256=PJ46v8WpcUY1nNSbPhbiY6B9ynB9bcakcDjopXTX2ME,787
|
|
47
|
+
user_scanner/social/snapchat.py,sha256=XEW_W4jEBX4AiHREcfHGstt97Ez3GI-3bKSzhtMyn28,1277
|
|
48
|
+
user_scanner/social/soundcloud.py,sha256=e2yU1w2fnH1EhzYed0kxgcqgWz0YoCQQFf6yKqhRPjM,1246
|
|
49
|
+
user_scanner/social/telegram.py,sha256=9IS-0pghMifNRmj62NcxCOvn23Hvg0AJJcuhCa_aXD4,765
|
|
50
|
+
user_scanner/social/threads.py,sha256=rK8Gm_riDdr0djo23tk38fNVVEBuC6nj2iTXvWrqXeE,951
|
|
51
|
+
user_scanner/social/x.py,sha256=sAnboHHZN2DWyKeds46GLZHxGG-G_bjzfVNIkblSHx8,1406
|
|
52
|
+
user_scanner/social/youtube.py,sha256=UPu584teg75P7FT05RFG3nobbHgPmzjr-ZwyN2sw6gw,1980
|
|
53
|
+
user_scanner/utils/update.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
|
+
user_scanner/utils/version.py,sha256=mPh24EwITyXgD3AMgbflRL180pS0JfrvuJdnoErOU34,623
|
|
55
|
+
user_scanner-1.0.8.1.dist-info/entry_points.txt,sha256=XqU3kssYZ0vXaPy5qYUOTCu4u-48Xie7QWFpBCYc7Nc,59
|
|
56
|
+
user_scanner-1.0.8.1.dist-info/licenses/LICENSE,sha256=XH1QyQG68zo1opDIZHTHcTAbe9XMzewvTaFTukcN9vc,1061
|
|
57
|
+
user_scanner-1.0.8.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
58
|
+
user_scanner-1.0.8.1.dist-info/METADATA,sha256=e14tVl6oz9iSik_h3jmapma4fdyyv3YYdWaYh-82sfY,5153
|
|
59
|
+
user_scanner-1.0.8.1.dist-info/RECORD,,
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
user_scanner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
user_scanner/__main__.py,sha256=r_8gq8wSRs3U7yUkTAv4e4MPopX0MUf_EbwedBTPs44,5082
|
|
3
|
-
user_scanner/version.json,sha256=JQUpDKvzaDU5eQcWcLXR7UejxgZkZcpBBYN1lgNmI6o,49
|
|
4
|
-
user_scanner/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
user_scanner/cli/banner.py,sha256=3t6owaDArERlvpcszA1Yi3dtksvh8a9tLyrxRowTC40,1499
|
|
6
|
-
user_scanner/community/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
user_scanner/community/coderlegion.py,sha256=W_bdjzdFPRgUrNFFlylvToSJ4AzaFCtTsUy_MRVDdSo,451
|
|
8
|
-
user_scanner/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
user_scanner/core/orchestrator.py,sha256=mN8aeFxgIZaWuDKbjgYLmFPtOwe8TtF2bQtAgcR7nb8,5682
|
|
10
|
-
user_scanner/creator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
user_scanner/creator/devto.py,sha256=mIACmG1a4eoctywxb5p04sI0YVi3dsjCRw9YVOFBEKQ,435
|
|
12
|
-
user_scanner/creator/hashnode.py,sha256=LBg8yRkB-WAVYavfeKWh6R2LWmUHMyXt02BLIXP-C4o,1378
|
|
13
|
-
user_scanner/creator/itch_io.py,sha256=d-s2k410aZtaYQXpTgaKsgQ488Ag9vGJj5buw7XM32I,556
|
|
14
|
-
user_scanner/creator/kaggle.py,sha256=QaXIG02OGxvQZEvwHm50RKNd7joxGOq0Ht3cFfrYEiU,445
|
|
15
|
-
user_scanner/creator/medium.py,sha256=zA8zw7hrH_JOXGM3oW7dfe5kldIRNyeBWZ8UCdAqZlg,1019
|
|
16
|
-
user_scanner/creator/patreon.py,sha256=aneIkwVSc7SFpE8H5HlSDMYQZc-97ZOtGZXrRb4PfWw,589
|
|
17
|
-
user_scanner/creator/producthunt.py,sha256=xgv-LJ97LLmyD2GRw4g3gylUMpOKUW0asbTnULbpZsE,1205
|
|
18
|
-
user_scanner/dev/__init__.py,sha256=qUR0eLwN-gO6oKk-1cmCVT4G_AxUHHMgpV3wJ7URXi4,7
|
|
19
|
-
user_scanner/dev/codeberg.py,sha256=Z6nV0_8xZhMiCcNn9Hn79VVh6y0ar9fqL7KS2b7IaDo,447
|
|
20
|
-
user_scanner/dev/cratesio.py,sha256=mJnlLJoMLlQ7f_95QD7LgH1xCj-e6FooOFkpYypBfG4,724
|
|
21
|
-
user_scanner/dev/dockerhub.py,sha256=sPEnomGiPM2mKv2HsA-9WxaXHjzz21A6ox3IXK1etLc,643
|
|
22
|
-
user_scanner/dev/github.py,sha256=jQ6q9V0snMUzGwIcbYZM_cJfiM8VZNYGm08dlDJqb6g,1113
|
|
23
|
-
user_scanner/dev/gitlab.py,sha256=FBwxrYWZXRkj55r_Dq8ZJLgLVLGrv5WU5k--31b_bz8,1243
|
|
24
|
-
user_scanner/dev/launchpad.py,sha256=N58ioX_dEHq2uwyyGrWnDKWwbqK9_RiuBQ1uWR5cDfg,799
|
|
25
|
-
user_scanner/dev/npmjs.py,sha256=K_bfLnehgaDlroiyMiIT801noRW-AyVISGd8xwMrWTM,1215
|
|
26
|
-
user_scanner/dev/replit.py,sha256=SI_i2l4w9tm2kBX4-cONBAT8dSynXoGEP4zcU8ngnh0,442
|
|
27
|
-
user_scanner/donation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
user_scanner/donation/buymeacoffee.py,sha256=8ZFuE8zjCclNP1C2RejDc4kMauGZpuYkxA1iiphSWKU,522
|
|
29
|
-
user_scanner/donation/liberapay.py,sha256=njClxpbRLZQ_L2-lUYCY6QFnF4IcwfCJPCIg1iEqo7M,1120
|
|
30
|
-
user_scanner/gaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
-
user_scanner/gaming/chess_com.py,sha256=LlZ-cn3wW8ysRSQ7FVnOofnJIgMbIDX_tGkdkNMhUwM,1282
|
|
32
|
-
user_scanner/gaming/minecraft.py,sha256=-mNhKTDwEETZv-IuLo1dHg2NfhFgb9w9-7zvP-HxtAU,601
|
|
33
|
-
user_scanner/gaming/monkeytype.py,sha256=uktLoHEV-wyjR-WsuVvgvrVCDVU3YpCISrhStM9odTU,1428
|
|
34
|
-
user_scanner/gaming/osu.py,sha256=L6LYOY1GyzQ2-KimGnvuT6BEGpY7K0hgV394sy8fvak,560
|
|
35
|
-
user_scanner/gaming/roblox.py,sha256=RnoME2NxMriILh1IiXQCe3J1l_G5Fs2S16YGKHCw3WY,1110
|
|
36
|
-
user_scanner/gaming/steam.py,sha256=-WtY-Wm68pJRW3fseayJfG3XqwGOzuUERuC5yLX16nQ,754
|
|
37
|
-
user_scanner/social/__init__.py,sha256=jaCkFwX1uYtF0ENifVwF8OfHrYYUTm64B9wlBq9BBfQ,9
|
|
38
|
-
user_scanner/social/bluesky.py,sha256=TlKONKmN2lvHRomQAqwkKzlIERSRKCbtPprcD9R_8CQ,1947
|
|
39
|
-
user_scanner/social/discord.py,sha256=iDnle44eyBTgeeyLb6jLysI4S-KRMNbaqDIVC0hCsMU,1135
|
|
40
|
-
user_scanner/social/instagram.py,sha256=GgmKGvi3meKdZ_nQJbJSBZDJTEKSoE6Cn4_VARmo62I,953
|
|
41
|
-
user_scanner/social/mastodon.py,sha256=l3Aixl7DCj_gtAiNzc3wA3pZxI-nP-zzRMuKkuwvTgM,578
|
|
42
|
-
user_scanner/social/pinterest.py,sha256=_Sditb8k1xRhplsN79RIEbZNlzfxIqFD6kGh3nAmVCE,677
|
|
43
|
-
user_scanner/social/reddit.py,sha256=yA7n7GFmS5mr02BOR4DeTmIT5GdnSCbUyaVarV9RMTk,700
|
|
44
|
-
user_scanner/social/snapchat.py,sha256=XEW_W4jEBX4AiHREcfHGstt97Ez3GI-3bKSzhtMyn28,1277
|
|
45
|
-
user_scanner/social/telegram.py,sha256=HU20t0A9jFijOUcdgzXfoNymxDBk2BhAqw7rIhSBfEQ,740
|
|
46
|
-
user_scanner/social/threads.py,sha256=rK8Gm_riDdr0djo23tk38fNVVEBuC6nj2iTXvWrqXeE,951
|
|
47
|
-
user_scanner/social/x.py,sha256=5UYBLmLuFpFeKQYqCkebQKB46GsfiQjV4flAdcrJAps,1591
|
|
48
|
-
user_scanner/social/youtube.py,sha256=nqW1XKZOefpPTDeCvqFTJXP24SjhntIQRlevwuSGLSk,2027
|
|
49
|
-
user_scanner/utils/update.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
-
user_scanner/utils/version.py,sha256=mPh24EwITyXgD3AMgbflRL180pS0JfrvuJdnoErOU34,623
|
|
51
|
-
user_scanner-1.0.7.0.dist-info/entry_points.txt,sha256=XqU3kssYZ0vXaPy5qYUOTCu4u-48Xie7QWFpBCYc7Nc,59
|
|
52
|
-
user_scanner-1.0.7.0.dist-info/licenses/LICENSE,sha256=XH1QyQG68zo1opDIZHTHcTAbe9XMzewvTaFTukcN9vc,1061
|
|
53
|
-
user_scanner-1.0.7.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
54
|
-
user_scanner-1.0.7.0.dist-info/METADATA,sha256=B4JLyOl64Ts11qVgfch25TYE0q3r_P2Rz0sFlv0Mpcc,4757
|
|
55
|
-
user_scanner-1.0.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|