user-scanner 1.0.5.0__py3-none-any.whl → 1.0.9.0__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/__init__.py +0 -1
- user_scanner/__main__.py +113 -64
- user_scanner/cli/banner.py +2 -0
- user_scanner/cli/printer.py +117 -0
- user_scanner/community/__init__.py +1 -0
- user_scanner/community/coderlegion.py +15 -14
- user_scanner/community/stackoverflow.py +35 -0
- user_scanner/core/orchestrator.py +167 -95
- user_scanner/core/result.py +128 -0
- user_scanner/core/utils.py +9 -0
- user_scanner/creator/devto.py +13 -11
- user_scanner/creator/hashnode.py +25 -28
- user_scanner/creator/itch_io.py +12 -15
- user_scanner/creator/kaggle.py +12 -11
- user_scanner/creator/medium.py +18 -22
- user_scanner/creator/patreon.py +12 -16
- user_scanner/creator/producthunt.py +38 -31
- user_scanner/dev/codeberg.py +13 -11
- user_scanner/dev/cratesio.py +19 -17
- user_scanner/dev/dockerhub.py +17 -15
- user_scanner/dev/github.py +46 -27
- user_scanner/dev/gitlab.py +18 -15
- user_scanner/dev/huggingface.py +19 -0
- user_scanner/dev/launchpad.py +19 -17
- user_scanner/dev/npmjs.py +21 -34
- user_scanner/dev/replit.py +13 -11
- user_scanner/donation/buymeacoffee.py +10 -12
- user_scanner/donation/liberapay.py +36 -0
- user_scanner/gaming/chess_com.py +17 -20
- user_scanner/gaming/minecraft.py +19 -0
- user_scanner/gaming/monkeytype.py +7 -12
- user_scanner/gaming/osu.py +13 -16
- user_scanner/gaming/roblox.py +35 -26
- user_scanner/gaming/steam.py +18 -19
- user_scanner/social/bluesky.py +20 -24
- user_scanner/social/discord.py +17 -21
- user_scanner/social/instagram.py +22 -20
- user_scanner/social/mastodon.py +12 -16
- user_scanner/social/pinterest.py +15 -13
- user_scanner/social/reddit.py +16 -13
- user_scanner/social/snapchat.py +28 -26
- user_scanner/social/soundcloud.py +43 -0
- user_scanner/social/telegram.py +19 -17
- user_scanner/social/threads.py +23 -21
- user_scanner/social/x.py +20 -28
- user_scanner/social/youtube.py +41 -47
- user_scanner/utils/update.py +0 -0
- user_scanner/utils/version.py +2 -0
- user_scanner/version.json +1 -1
- {user_scanner-1.0.5.0.dist-info → user_scanner-1.0.9.0.dist-info}/METADATA +58 -23
- user_scanner-1.0.9.0.dist-info/RECORD +61 -0
- user_scanner-1.0.5.0.dist-info/RECORD +0 -52
- {user_scanner-1.0.5.0.dist-info → user_scanner-1.0.9.0.dist-info}/WHEEL +0 -0
- {user_scanner-1.0.5.0.dist-info → user_scanner-1.0.9.0.dist-info}/entry_points.txt +0 -0
- {user_scanner-1.0.5.0.dist-info → user_scanner-1.0.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,93 +1,144 @@
|
|
|
1
1
|
import importlib
|
|
2
|
-
import
|
|
2
|
+
import importlib.util
|
|
3
3
|
from colorama import Fore, Style
|
|
4
|
-
import
|
|
5
|
-
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
+
from itertools import permutations
|
|
6
6
|
import httpx
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from user_scanner.cli.printer import Printer
|
|
9
|
+
from user_scanner.core.result import Result, AnyResult
|
|
10
|
+
from typing import Callable, Dict, List
|
|
11
|
+
from user_scanner.core.utils import get_site_name, is_last_value
|
|
12
12
|
|
|
13
|
-
def load_modules(package):
|
|
14
13
|
|
|
14
|
+
def load_modules(category_path: Path):
|
|
15
15
|
modules = []
|
|
16
|
-
for
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
for file in category_path.glob("*.py"):
|
|
17
|
+
if file.name == "__init__.py":
|
|
18
|
+
continue
|
|
19
|
+
spec = importlib.util.spec_from_file_location(file.stem, str(file))
|
|
20
|
+
module = importlib.util.module_from_spec(spec)
|
|
21
|
+
spec.loader.exec_module(module)
|
|
22
|
+
|
|
23
|
+
modules.append(module)
|
|
22
24
|
return modules
|
|
23
25
|
|
|
24
|
-
def worker_single(module, username, i):
|
|
25
|
-
global print_queue
|
|
26
26
|
|
|
27
|
+
def load_categories() -> Dict[str, Path]:
|
|
28
|
+
root = Path(__file__).resolve().parent.parent # Should be user_scanner
|
|
29
|
+
categories = {}
|
|
30
|
+
|
|
31
|
+
for subfolder in root.iterdir():
|
|
32
|
+
if subfolder.is_dir() and \
|
|
33
|
+
not subfolder.name.lower() in ["cli", "utils", "core"] and \
|
|
34
|
+
not "__" in subfolder.name: # Removes __pycache__
|
|
35
|
+
categories[subfolder.name] = subfolder.resolve()
|
|
36
|
+
|
|
37
|
+
return categories
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def find_module(name: str):
|
|
41
|
+
name = name.lower()
|
|
42
|
+
|
|
43
|
+
matches = [
|
|
44
|
+
module
|
|
45
|
+
for category_path in load_categories().values()
|
|
46
|
+
for module in load_modules(category_path)
|
|
47
|
+
if module.__name__.split(".")[-1].lower() == name
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
return matches
|
|
51
|
+
|
|
52
|
+
def find_category(module) -> str | None:
|
|
53
|
+
|
|
54
|
+
module_file = getattr(module, '__file__', None)
|
|
55
|
+
if not module_file:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
category = Path(module_file).parent.name.lower()
|
|
59
|
+
categories = load_categories()
|
|
60
|
+
if category in categories:
|
|
61
|
+
return category.capitalize()
|
|
62
|
+
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def worker_single(module, username: str) -> Result:
|
|
27
68
|
func = next((getattr(module, f) for f in dir(module)
|
|
28
69
|
if f.startswith("validate_") and callable(getattr(module, f))), None)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
70
|
+
|
|
71
|
+
site_name = get_site_name(module)
|
|
72
|
+
|
|
73
|
+
if not func:
|
|
74
|
+
return Result.error(f"{site_name} has no validate_ function", site_name=site_name, username=username)
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
result: Result = func(username)
|
|
78
|
+
result.update(site_name=site_name, username=username)
|
|
79
|
+
return result
|
|
80
|
+
except Exception as e:
|
|
81
|
+
return Result.error(e, site_name=site_name, username=username)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def run_module_single(module, username: str, printer: Printer, last: bool = True) -> List[Result]:
|
|
85
|
+
result = worker_single(module, username)
|
|
86
|
+
|
|
87
|
+
category = find_category(module)
|
|
88
|
+
if category:
|
|
89
|
+
result.update(category=category)
|
|
90
|
+
|
|
91
|
+
site_name = get_site_name(module)
|
|
92
|
+
msg = printer.get_result_output(result)
|
|
93
|
+
if last == False and printer.is_json:
|
|
94
|
+
msg += ","
|
|
95
|
+
print(msg)
|
|
96
|
+
|
|
97
|
+
return [result]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run_checks_category(category_path: Path, username: str, printer: Printer, last: bool = True) -> List[Result]:
|
|
102
|
+
modules = load_modules(category_path)
|
|
103
|
+
|
|
104
|
+
category_name = category_path.stem.capitalize()
|
|
105
|
+
if printer.is_console:
|
|
106
|
+
print(f"\n{Fore.MAGENTA}== {category_name} SITES =={Style.RESET_ALL}")
|
|
107
|
+
|
|
108
|
+
results = []
|
|
109
|
+
|
|
110
|
+
with ThreadPoolExecutor(max_workers=10) as executor:
|
|
111
|
+
exec_map = executor.map(lambda m: worker_single(m, username), modules)
|
|
112
|
+
for i, result in enumerate(exec_map):
|
|
113
|
+
result.update(category = category_name)
|
|
114
|
+
results.append(result)
|
|
115
|
+
|
|
116
|
+
is_last = last and is_last_value(modules, i)
|
|
117
|
+
site_name = get_site_name(modules[i])
|
|
118
|
+
msg = printer.get_result_output(result)
|
|
119
|
+
if is_last == False and printer.is_json:
|
|
120
|
+
msg += ","
|
|
121
|
+
print(msg)
|
|
122
|
+
|
|
123
|
+
return results
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def run_checks(username: str, printer: Printer, last: bool = True) -> List[Result]:
|
|
127
|
+
if printer.is_console:
|
|
128
|
+
print(f"\n{Fore.CYAN} Checking username: {username}{Style.RESET_ALL}")
|
|
129
|
+
|
|
130
|
+
results = []
|
|
131
|
+
|
|
132
|
+
categories = list(load_categories().values())
|
|
133
|
+
for i, category_path in enumerate(categories):
|
|
134
|
+
last_cat: int = last and (i == len(categories) - 1)
|
|
135
|
+
temp = run_checks_category(category_path, username, printer, last_cat)
|
|
136
|
+
results.extend(temp)
|
|
137
|
+
|
|
138
|
+
return results
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def make_get_request(url: str, **kwargs) -> httpx.Response:
|
|
91
142
|
"""Simple wrapper to **httpx.get** that predefines headers and timeout"""
|
|
92
143
|
if not "headers" in kwargs:
|
|
93
144
|
kwargs["headers"] = {
|
|
@@ -103,38 +154,59 @@ def make_get_request(url, **kwargs):
|
|
|
103
154
|
|
|
104
155
|
return httpx.get(url, **kwargs)
|
|
105
156
|
|
|
106
|
-
|
|
157
|
+
|
|
158
|
+
def generic_validate(url: str, func: Callable[[httpx.Response], AnyResult], **kwargs) -> AnyResult:
|
|
107
159
|
"""
|
|
108
160
|
A generic validate function that makes a request and executes the provided function on the response.
|
|
109
161
|
"""
|
|
110
162
|
try:
|
|
111
163
|
response = make_get_request(url, **kwargs)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return
|
|
115
|
-
except Exception:
|
|
116
|
-
return
|
|
117
|
-
|
|
118
|
-
|
|
164
|
+
result = func(response)
|
|
165
|
+
result.url = url
|
|
166
|
+
return result
|
|
167
|
+
except Exception as e:
|
|
168
|
+
return Result.error(e, url=url)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def status_validate(url: str, available: int | List[int], taken: int | List[int], **kwargs) -> Result:
|
|
119
172
|
"""
|
|
120
173
|
Function that takes a **url** and **kwargs** for the request and
|
|
121
174
|
checks if the request status matches the availabe or taken.
|
|
122
175
|
**Available** and **Taken** must either be whole numbers or lists of whole numbers.
|
|
123
176
|
"""
|
|
124
|
-
def inner(response):
|
|
125
|
-
#Checks if a number is equal or is contained inside
|
|
126
|
-
contains
|
|
127
|
-
|
|
177
|
+
def inner(response: httpx.Response):
|
|
178
|
+
# Checks if a number is equal or is contained inside
|
|
179
|
+
def contains(a, b): return (isinstance(a, list) and b in a) or (a == b)
|
|
128
180
|
status = response.status_code
|
|
129
181
|
available_value = contains(available, status)
|
|
130
182
|
taken_value = contains(taken, status)
|
|
131
183
|
|
|
132
184
|
if available_value and taken_value:
|
|
133
|
-
|
|
185
|
+
# Can't be both available and taken
|
|
186
|
+
return Result.error("Invalid status match. Report this on Github.")
|
|
134
187
|
elif available_value:
|
|
135
|
-
return
|
|
188
|
+
return Result.available()
|
|
136
189
|
elif taken_value:
|
|
137
|
-
return
|
|
138
|
-
return
|
|
190
|
+
return Result.taken()
|
|
191
|
+
return Result.error("Status didn't match. Report this on Github.")
|
|
139
192
|
|
|
140
193
|
return generic_validate(url, inner, **kwargs)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def generate_permutations(username, pattern, limit=None):
|
|
197
|
+
"""
|
|
198
|
+
Generate all order-based permutations of characters in `pattern`
|
|
199
|
+
appended after `username`.
|
|
200
|
+
"""
|
|
201
|
+
permutations_set = {username}
|
|
202
|
+
|
|
203
|
+
chars = list(pattern)
|
|
204
|
+
|
|
205
|
+
# generate permutations of length 1 → len(chars)
|
|
206
|
+
for r in range(1, len(chars) + 1):
|
|
207
|
+
for combo in permutations(chars, r):
|
|
208
|
+
permutations_set.add(username + ''.join(combo))
|
|
209
|
+
if limit and len(permutations_set) >= limit:
|
|
210
|
+
return list(permutations_set)[:limit]
|
|
211
|
+
|
|
212
|
+
return sorted(permutations_set)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
DEBUG_MSG = """Result {{
|
|
5
|
+
status: {status},
|
|
6
|
+
reason: "{reason}",
|
|
7
|
+
username: "{username}",
|
|
8
|
+
site_name: "{site_name}",
|
|
9
|
+
category: "{category}",
|
|
10
|
+
}}"""
|
|
11
|
+
|
|
12
|
+
JSON_TEMPLATE = """{{
|
|
13
|
+
\t"username": "{username}",
|
|
14
|
+
\t"category": "{category}",
|
|
15
|
+
\t"site_name": "{site_name}",
|
|
16
|
+
\t"status": "{status}",
|
|
17
|
+
\t"reason": "{reason}"
|
|
18
|
+
}}"""
|
|
19
|
+
|
|
20
|
+
CSV_TEMPLATE = "{username},{category},{site_name},{status},{reason}"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def humanize_exception(e: Exception) -> str:
|
|
24
|
+
msg = str(e).lower()
|
|
25
|
+
|
|
26
|
+
if "10054" in msg:
|
|
27
|
+
return "Connection closed by remote server"
|
|
28
|
+
if "11001" in msg:
|
|
29
|
+
return "Could not resolve hostname"
|
|
30
|
+
|
|
31
|
+
return str(e)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Status(Enum):
|
|
35
|
+
TAKEN = 0
|
|
36
|
+
AVAILABLE = 1
|
|
37
|
+
ERROR = 2
|
|
38
|
+
|
|
39
|
+
def __str__(self):
|
|
40
|
+
return super().__str__().split(".")[1].capitalize()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Result:
|
|
44
|
+
def __init__(self, status: Status, reason: str | Exception | None = None, **kwargs):
|
|
45
|
+
self.status = status
|
|
46
|
+
self.reason = reason
|
|
47
|
+
|
|
48
|
+
self.username = None
|
|
49
|
+
self.site_name = None
|
|
50
|
+
self.category = None
|
|
51
|
+
self.update(**kwargs)
|
|
52
|
+
|
|
53
|
+
def update(self, **kwargs):
|
|
54
|
+
for field in ("username", "site_name", "category"):
|
|
55
|
+
if field in kwargs and kwargs[field] is not None:
|
|
56
|
+
setattr(self, field, kwargs[field])
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def taken(cls, **kwargs):
|
|
60
|
+
return cls(Status.TAKEN, **kwargs)
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def available(cls, **kwargs):
|
|
64
|
+
return cls(Status.AVAILABLE, **kwargs)
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def error(cls, reason: str | Exception | None = None, **kwargs):
|
|
68
|
+
return cls(Status.ERROR, reason, **kwargs)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_number(cls, i: int, reason: str | Exception | None = None):
|
|
72
|
+
try:
|
|
73
|
+
status = Status(i)
|
|
74
|
+
except ValueError:
|
|
75
|
+
return cls(Status.ERROR, "Invalid status. Please contact maintainers.")
|
|
76
|
+
|
|
77
|
+
return cls(status, reason if status == Status.ERROR else None)
|
|
78
|
+
|
|
79
|
+
def to_number(self) -> int:
|
|
80
|
+
return self.status.value
|
|
81
|
+
|
|
82
|
+
def has_reason(self) -> bool:
|
|
83
|
+
return self.reason != None
|
|
84
|
+
|
|
85
|
+
def get_reason(self) -> str:
|
|
86
|
+
if self.reason == None:
|
|
87
|
+
return ""
|
|
88
|
+
if isinstance(self.reason, str):
|
|
89
|
+
return self.reason
|
|
90
|
+
# Format the exception
|
|
91
|
+
msg = humanize_exception(self.reason)
|
|
92
|
+
return f"{type(self.reason).__name__}: {msg.capitalize()}"
|
|
93
|
+
|
|
94
|
+
def as_dict(self) -> dict:
|
|
95
|
+
return {
|
|
96
|
+
"status": self.status,
|
|
97
|
+
"reason": self.get_reason(),
|
|
98
|
+
"username": self.username,
|
|
99
|
+
"site_name": self.site_name,
|
|
100
|
+
"category": self.category
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
def debug(self) -> str:
|
|
104
|
+
return DEBUG_MSG.format(**self.as_dict())
|
|
105
|
+
|
|
106
|
+
def to_json(self) -> str:
|
|
107
|
+
return JSON_TEMPLATE.format(**self.as_dict())
|
|
108
|
+
|
|
109
|
+
def to_csv(self) -> str:
|
|
110
|
+
return CSV_TEMPLATE.format(**self.as_dict())
|
|
111
|
+
|
|
112
|
+
def __str__(self):
|
|
113
|
+
return self.get_reason()
|
|
114
|
+
|
|
115
|
+
def __eq__(self, other):
|
|
116
|
+
if isinstance(other, Status):
|
|
117
|
+
return self.status == other
|
|
118
|
+
|
|
119
|
+
if isinstance(other, Result):
|
|
120
|
+
return self.status == other.status
|
|
121
|
+
|
|
122
|
+
if isinstance(other, int):
|
|
123
|
+
return self.to_number() == other
|
|
124
|
+
|
|
125
|
+
return NotImplemented
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
AnyResult = Literal[0, 1, 2] | Result
|
user_scanner/creator/devto.py
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
from
|
|
1
|
+
from user_scanner.core.orchestrator import status_validate
|
|
2
|
+
|
|
2
3
|
|
|
3
4
|
def validate_devto(user):
|
|
4
|
-
|
|
5
|
+
url = f"https://dev.to/{user}"
|
|
6
|
+
|
|
7
|
+
return status_validate(url, 404, 200, follow_redirects=True)
|
|
5
8
|
|
|
6
|
-
return status_validate(url, 404, 200, follow_redirects = True)
|
|
7
9
|
|
|
8
10
|
if __name__ == "__main__":
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
user = input("Username?: ").strip()
|
|
12
|
+
result = validate_devto(user)
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
if result == 1:
|
|
15
|
+
print("Available!")
|
|
16
|
+
elif result == 0:
|
|
17
|
+
print("Unavailable!")
|
|
18
|
+
else:
|
|
19
|
+
print("Error occurred!")
|
user_scanner/creator/hashnode.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import httpx
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
from user_scanner.core.result import Result
|
|
3
|
+
|
|
4
4
|
|
|
5
5
|
def validate_hashnode(user):
|
|
6
6
|
url = "https://hashnode.com/utility/ajax/check-username"
|
|
7
7
|
|
|
8
8
|
payload = {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
"username": user,
|
|
10
|
+
"name": "Dummy Dummy"
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
headers = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
'User-Agent': "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Mobile Safari/537.36",
|
|
15
|
+
'Accept': "application/json",
|
|
16
|
+
'Content-Type': "application/json",
|
|
17
|
+
'Origin': "https://hashnode.com",
|
|
18
|
+
'Referer': "https://hashnode.com/signup",
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
try:
|
|
@@ -26,29 +26,26 @@ def validate_hashnode(user):
|
|
|
26
26
|
|
|
27
27
|
if 'status' in data:
|
|
28
28
|
if data['status'] == 1:
|
|
29
|
-
return
|
|
29
|
+
return Result.available()
|
|
30
30
|
elif data['status'] == 0:
|
|
31
|
-
return
|
|
31
|
+
return Result.taken()
|
|
32
32
|
|
|
33
|
-
return
|
|
33
|
+
return Result.error("Status not found")
|
|
34
34
|
|
|
35
35
|
else:
|
|
36
|
-
|
|
36
|
+
return Result.error("Invalid status code")
|
|
37
|
+
|
|
38
|
+
except Exception as e:
|
|
39
|
+
return Result.error(e)
|
|
37
40
|
|
|
38
|
-
except (ConnectError, TimeoutException):
|
|
39
|
-
return 2
|
|
40
|
-
except json.JSONDecodeError:
|
|
41
|
-
return 2
|
|
42
|
-
except Exception:
|
|
43
|
-
return 2
|
|
44
41
|
|
|
45
42
|
if __name__ == "__main__":
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
user = input("Username?: ").strip()
|
|
44
|
+
result = validate_hashnode(user)
|
|
45
|
+
|
|
46
|
+
if result == 1:
|
|
47
|
+
print("Available!")
|
|
48
|
+
elif result == 0:
|
|
49
|
+
print("Unavailable!")
|
|
50
|
+
else:
|
|
51
|
+
print("Error occurred!")
|
user_scanner/creator/itch_io.py
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
from
|
|
1
|
+
from user_scanner.core.orchestrator import status_validate
|
|
2
2
|
|
|
3
|
-
def validate_itch_io(user):
|
|
4
|
-
"""
|
|
5
|
-
Checks if a itch.io username is available.
|
|
6
|
-
Returns: 1 -> available, 0 -> taken, 2 -> error
|
|
7
|
-
"""
|
|
8
3
|
|
|
4
|
+
def validate_itch_io(user):
|
|
9
5
|
url = f"https://{user}.itch.io"
|
|
10
6
|
|
|
11
|
-
return
|
|
7
|
+
return status_validate(url, 404, 200, follow_redirects=True)
|
|
8
|
+
|
|
12
9
|
|
|
13
10
|
if __name__ == "__main__":
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
user = input("Username?: ").strip()
|
|
12
|
+
result = validate_itch_io(user)
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
if result == 1:
|
|
15
|
+
print("Available!")
|
|
16
|
+
elif result == 0:
|
|
17
|
+
print("Unavailable!")
|
|
18
|
+
else:
|
|
19
|
+
print("Error occurred!")
|
user_scanner/creator/kaggle.py
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
from
|
|
1
|
+
from user_scanner.core.orchestrator import status_validate
|
|
2
|
+
|
|
2
3
|
|
|
3
4
|
def validate_kaggle(user):
|
|
4
|
-
|
|
5
|
+
url = f"https://www.kaggle.com/{user}"
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
return status_validate(url, 404, 200, follow_redirects=True)
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
if __name__ == "__main__":
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
user = input("Username?: ").strip()
|
|
12
|
+
result = validate_kaggle(user)
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
if result == 1:
|
|
15
|
+
print("Available!")
|
|
16
|
+
elif result == 0:
|
|
17
|
+
print("Unavailable!")
|
|
18
|
+
else:
|
|
19
|
+
print("Error occurred!")
|
user_scanner/creator/medium.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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
|
def validate_medium(user):
|
|
5
6
|
url = f"https://medium.com/@{user}"
|
|
@@ -9,33 +10,28 @@ def validate_medium(user):
|
|
|
9
10
|
'Accept': "text/html",
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
response = httpx.get(url, headers=headers, timeout=3.0)
|
|
14
|
-
|
|
13
|
+
def process(response):
|
|
15
14
|
if response.status_code == 200:
|
|
16
15
|
html_text = response.text
|
|
17
16
|
|
|
18
|
-
|
|
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()
|
|
24
|
+
|
|
25
|
+
return generic_validate(url, process, headers=headers)
|
|
26
26
|
|
|
27
|
-
except (ConnectError, TimeoutException):
|
|
28
|
-
return 2
|
|
29
|
-
except Exception:
|
|
30
|
-
return 2
|
|
31
27
|
|
|
32
28
|
if __name__ == "__main__":
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
user = input("Username?: ").strip()
|
|
30
|
+
result = validate_medium(user)
|
|
31
|
+
|
|
32
|
+
if result == 1:
|
|
33
|
+
print("Available!")
|
|
34
|
+
elif result == 0:
|
|
35
|
+
print("Unavailable!")
|
|
36
|
+
else:
|
|
37
|
+
print("Error occurred!")
|