user-scanner 1.0.6.0__py3-none-any.whl → 1.0.7.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/__main__.py +63 -44
- user_scanner/core/orchestrator.py +51 -21
- user_scanner/version.json +1 -1
- {user_scanner-1.0.6.0.dist-info → user_scanner-1.0.7.0.dist-info}/METADATA +20 -2
- {user_scanner-1.0.6.0.dist-info → user_scanner-1.0.7.0.dist-info}/RECORD +8 -8
- {user_scanner-1.0.6.0.dist-info → user_scanner-1.0.7.0.dist-info}/WHEEL +0 -0
- {user_scanner-1.0.6.0.dist-info → user_scanner-1.0.7.0.dist-info}/entry_points.txt +0 -0
- {user_scanner-1.0.6.0.dist-info → user_scanner-1.0.7.0.dist-info}/licenses/LICENSE +0 -0
user_scanner/__main__.py
CHANGED
|
@@ -1,38 +1,22 @@
|
|
|
1
1
|
import argparse
|
|
2
|
+
import time
|
|
2
3
|
import re
|
|
3
|
-
from user_scanner.core.orchestrator import run_checks, load_modules
|
|
4
|
+
from user_scanner.core.orchestrator import run_checks, load_modules , generate_permutations, load_categories
|
|
4
5
|
from colorama import Fore, Style
|
|
5
6
|
from .cli import banner
|
|
6
7
|
from .cli.banner import print_banner
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
"dev": "dev",
|
|
10
|
-
"social": "social",
|
|
11
|
-
"creator": "creator",
|
|
12
|
-
"community": "community",
|
|
13
|
-
"gaming": "gaming",
|
|
14
|
-
"donation": "donation"
|
|
15
|
-
}
|
|
16
|
-
|
|
9
|
+
MAX_PERMUTATIONS_LIMIT = 100 # To prevent excessive generation
|
|
17
10
|
|
|
18
11
|
def list_modules(category=None):
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"dev": dev,
|
|
22
|
-
"social": social,
|
|
23
|
-
"creator": creator,
|
|
24
|
-
"community": community,
|
|
25
|
-
"gaming": gaming,
|
|
26
|
-
"donation": donation
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
categories_to_list = [category] if category else packages.keys()
|
|
12
|
+
categories = load_categories()
|
|
13
|
+
categories_to_list = [category] if category else categories.keys()
|
|
30
14
|
|
|
31
15
|
for cat_name in categories_to_list:
|
|
32
|
-
|
|
33
|
-
modules = load_modules(
|
|
16
|
+
path = categories[cat_name]
|
|
17
|
+
modules = load_modules(path)
|
|
34
18
|
print(Fore.MAGENTA +
|
|
35
|
-
|
|
19
|
+
f"\n== {cat_name.upper()} SITES =={Style.RESET_ALL}")
|
|
36
20
|
for module in modules:
|
|
37
21
|
site_name = module.__name__.split(".")[-1]
|
|
38
22
|
print(f" - {site_name}")
|
|
@@ -47,7 +31,7 @@ def main():
|
|
|
47
31
|
"-u", "--username", help="Username to scan across platforms"
|
|
48
32
|
)
|
|
49
33
|
parser.add_argument(
|
|
50
|
-
"-c", "--category", choices=
|
|
34
|
+
"-c", "--category", choices=load_categories().keys(),
|
|
51
35
|
help="Scan all platforms in a category"
|
|
52
36
|
)
|
|
53
37
|
parser.add_argument(
|
|
@@ -59,16 +43,28 @@ def main():
|
|
|
59
43
|
parser.add_argument(
|
|
60
44
|
"-v", "--verbose", action="store_true", help="Enable verbose output"
|
|
61
45
|
)
|
|
62
|
-
|
|
46
|
+
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"-p", "--permute",type=str,help="Generate username permutations using a string pattern (e.g -p 234)"
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"-s", "--stop",type=int,default=MAX_PERMUTATIONS_LIMIT,help="Limit the number of username permutations generated"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"-d", "--delay",type=float,default=0,help="Delay in seconds between requests (recommended: 1-2 seconds)"
|
|
56
|
+
)
|
|
57
|
+
|
|
63
58
|
args = parser.parse_args()
|
|
64
|
-
|
|
65
|
-
if args.module and "." in args.module:
|
|
66
|
-
args.module = args.module.replace(".", "_")
|
|
67
|
-
|
|
59
|
+
|
|
68
60
|
if args.list:
|
|
69
61
|
list_modules(args.category)
|
|
70
62
|
return
|
|
71
|
-
|
|
63
|
+
|
|
64
|
+
if not args.username:
|
|
65
|
+
parser.print_help()
|
|
66
|
+
return
|
|
67
|
+
|
|
72
68
|
# Special username checks before run
|
|
73
69
|
if (args.module == "x" or args.category == "social"):
|
|
74
70
|
if re.search(r"[^a-zA-Z0-9._-]", args.username):
|
|
@@ -78,37 +74,60 @@ def main():
|
|
|
78
74
|
if re.search(r"[^a-zA-Z0-9\.-]", args.username):
|
|
79
75
|
print(
|
|
80
76
|
Fore.RED + f"[!] Username '{args.username}' contains unsupported special characters. Bluesky will throw error. (Supported: only hyphens and digits)" + Style.RESET_ALL + "\n")
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
print_banner()
|
|
78
|
+
|
|
79
|
+
if args.permute and args.delay == 0:
|
|
80
|
+
print(
|
|
81
|
+
Fore.YELLOW
|
|
82
|
+
+ "[!] Warning: You're generating multiple usernames with NO delay between requests. "
|
|
83
|
+
"This may trigger rate limits or IP bans. Use --delay 1 or higher. (Use only if the sites throw errors otherwise ignore)\n"
|
|
84
|
+
+ Style.RESET_ALL)
|
|
85
|
+
|
|
86
|
+
usernames = [args.username] # Default single username list
|
|
87
|
+
|
|
88
|
+
#Added permutation support , generate all possible permutation of given sequence.
|
|
89
|
+
if args.permute:
|
|
90
|
+
usernames = generate_permutations(args.username, args.permute , args.stop)
|
|
91
|
+
print(Fore.CYAN + f"[+] Generated {len(usernames)} username permutations" + Style.RESET_ALL)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if args.module and "." in args.module:
|
|
96
|
+
args.module = args.module.replace(".", "_")
|
|
86
97
|
|
|
87
|
-
from user_scanner import dev, social, creator, community, gaming, donation
|
|
88
98
|
|
|
89
99
|
if args.module:
|
|
90
100
|
# Single module search across all categories
|
|
91
|
-
packages = [dev, social, creator, community, gaming, donation]
|
|
92
101
|
found = False
|
|
93
|
-
for
|
|
94
|
-
modules = load_modules(
|
|
102
|
+
for cat_path in load_categories().values():
|
|
103
|
+
modules = load_modules(cat_path)
|
|
95
104
|
for module in modules:
|
|
96
105
|
site_name = module.__name__.split(".")[-1]
|
|
97
106
|
if site_name.lower() == args.module.lower():
|
|
98
107
|
from user_scanner.core.orchestrator import run_module_single
|
|
99
|
-
|
|
108
|
+
for name in usernames: # <-- permutation support here
|
|
109
|
+
run_module_single(module, name)
|
|
110
|
+
if args.delay > 0:
|
|
111
|
+
time.sleep(args.delay)
|
|
100
112
|
found = True
|
|
101
113
|
if not found:
|
|
102
114
|
print(
|
|
103
115
|
Fore.RED + f"[!] Module '{args.module}' not found in any category." + Style.RESET_ALL)
|
|
104
116
|
elif args.category:
|
|
105
117
|
# Category-wise scan
|
|
106
|
-
category_package =
|
|
118
|
+
category_package = load_categories().get(args.category)
|
|
107
119
|
from user_scanner.core.orchestrator import run_checks_category
|
|
108
|
-
|
|
120
|
+
|
|
121
|
+
for name in usernames: # <-- permutation support here
|
|
122
|
+
run_checks_category(category_package, name, args.verbose)
|
|
123
|
+
if args.delay > 0:
|
|
124
|
+
time.sleep(args.delay)
|
|
109
125
|
else:
|
|
110
126
|
# Full scan
|
|
111
|
-
|
|
127
|
+
for name in usernames:
|
|
128
|
+
run_checks(name)
|
|
129
|
+
if args.delay > 0:
|
|
130
|
+
time.sleep(args.delay)
|
|
112
131
|
|
|
113
132
|
|
|
114
133
|
if __name__ == "__main__":
|
|
@@ -2,24 +2,41 @@ import importlib
|
|
|
2
2
|
import pkgutil
|
|
3
3
|
from colorama import Fore, Style
|
|
4
4
|
import threading
|
|
5
|
-
|
|
5
|
+
from itertools import permutations
|
|
6
6
|
import httpx
|
|
7
7
|
from httpx import ConnectError, TimeoutException
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Dict
|
|
8
10
|
|
|
9
11
|
lock = threading.Condition()
|
|
10
12
|
# Basically which thread is the one to print
|
|
11
13
|
print_queue = 0
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
def
|
|
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
|
+
def load_modules(category_path: Path):
|
|
15
30
|
|
|
16
31
|
modules = []
|
|
17
|
-
for
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
for file in category_path.glob("*.py"):
|
|
33
|
+
if file.name == "__init__.py":
|
|
34
|
+
continue
|
|
35
|
+
spec = importlib.util.spec_from_file_location(file.stem, str(file))
|
|
36
|
+
module = importlib.util.module_from_spec(spec)
|
|
37
|
+
spec.loader.exec_module(module)
|
|
38
|
+
|
|
39
|
+
modules.append(module)
|
|
23
40
|
return modules
|
|
24
41
|
|
|
25
42
|
|
|
@@ -37,11 +54,11 @@ def worker_single(module, username, i):
|
|
|
37
54
|
try:
|
|
38
55
|
result = func(username)
|
|
39
56
|
if result == 1:
|
|
40
|
-
output = f" {Fore.GREEN}[✔] {site_name}: Available{Style.RESET_ALL}"
|
|
57
|
+
output = f" {Fore.GREEN}[✔] {site_name} ({username}): Available{Style.RESET_ALL}"
|
|
41
58
|
elif result == 0:
|
|
42
|
-
output = f" {Fore.RED}[✘] {site_name}: Taken{Style.RESET_ALL}"
|
|
59
|
+
output = f" {Fore.RED}[✘] {site_name} ({username}): Taken{Style.RESET_ALL}"
|
|
43
60
|
else:
|
|
44
|
-
output = f" {Fore.YELLOW}[!] {site_name}: Error{Style.RESET_ALL}"
|
|
61
|
+
output = f" {Fore.YELLOW}[!] {site_name} ({username}): Error{Style.RESET_ALL}"
|
|
45
62
|
except Exception as e:
|
|
46
63
|
output = f" {Fore.YELLOW}[!] {site_name}: Exception - {e}{Style.RESET_ALL}"
|
|
47
64
|
else:
|
|
@@ -62,11 +79,11 @@ def run_module_single(module, username):
|
|
|
62
79
|
worker_single(module, username, print_queue)
|
|
63
80
|
|
|
64
81
|
|
|
65
|
-
def run_checks_category(
|
|
82
|
+
def run_checks_category(category_path:Path, username:str, verbose=False):
|
|
66
83
|
global print_queue
|
|
67
84
|
|
|
68
|
-
modules = load_modules(
|
|
69
|
-
category_name =
|
|
85
|
+
modules = load_modules(category_path)
|
|
86
|
+
category_name = category_path.stem.capitalize()
|
|
70
87
|
print(f"{Fore.MAGENTA}== {category_name} SITES =={Style.RESET_ALL}")
|
|
71
88
|
|
|
72
89
|
print_queue = 0
|
|
@@ -82,14 +99,10 @@ def run_checks_category(package, username, verbose=False):
|
|
|
82
99
|
|
|
83
100
|
|
|
84
101
|
def run_checks(username):
|
|
85
|
-
from user_scanner import dev, social, creator, community, gaming, donation
|
|
86
|
-
|
|
87
|
-
packages = [dev, social, creator, community, gaming, donation]
|
|
88
|
-
|
|
89
102
|
print(f"\n{Fore.CYAN} Checking username: {username}{Style.RESET_ALL}\n")
|
|
90
103
|
|
|
91
|
-
for
|
|
92
|
-
run_checks_category(
|
|
104
|
+
for category_path in load_categories().values():
|
|
105
|
+
run_checks_category(category_path, username)
|
|
93
106
|
print()
|
|
94
107
|
|
|
95
108
|
|
|
@@ -132,7 +145,6 @@ def status_validate(url, available, taken, **kwargs):
|
|
|
132
145
|
def inner(response):
|
|
133
146
|
# Checks if a number is equal or is contained inside
|
|
134
147
|
def contains(a, b): return (isinstance(a, list) and b in a) or (a == b)
|
|
135
|
-
|
|
136
148
|
status = response.status_code
|
|
137
149
|
available_value = contains(available, status)
|
|
138
150
|
taken_value = contains(taken, status)
|
|
@@ -146,3 +158,21 @@ def status_validate(url, available, taken, **kwargs):
|
|
|
146
158
|
return 2
|
|
147
159
|
|
|
148
160
|
return generic_validate(url, inner, **kwargs)
|
|
161
|
+
|
|
162
|
+
def generate_permutations(username, pattern, limit=None):
|
|
163
|
+
"""
|
|
164
|
+
Generate all order-based permutations of characters in `pattern`
|
|
165
|
+
appended after `username`.
|
|
166
|
+
"""
|
|
167
|
+
permutations_set = {username}
|
|
168
|
+
|
|
169
|
+
chars = list(pattern)
|
|
170
|
+
|
|
171
|
+
# generate permutations of length 1 → len(chars)
|
|
172
|
+
for r in range(1, len(chars) + 1):
|
|
173
|
+
for combo in permutations(chars, r):
|
|
174
|
+
permutations_set.add(username + ''.join(combo))
|
|
175
|
+
if limit and len(permutations_set) >= limit:
|
|
176
|
+
return list(permutations_set)[:limit]
|
|
177
|
+
|
|
178
|
+
return sorted(permutations_set)
|
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.7.0
|
|
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.7.0-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" />
|
|
@@ -34,6 +34,7 @@ Perfect for finding a **unique username** across GitHub, Twitter, Reddit, Instag
|
|
|
34
34
|
- ✅ Check usernames across **social networks**, **developer platforms**, and **creator communities**.
|
|
35
35
|
- ✅ Clear **Available / Taken / Error** output for each platform.
|
|
36
36
|
- ✅ Fully modular: add new platform modules easily.
|
|
37
|
+
- ✅ Wildcard-based username permutations for automatic variation generation
|
|
37
38
|
- ✅ Command-line interface ready: works directly after `pip install`.
|
|
38
39
|
- ✅ Can be used as username OSINT tool.
|
|
39
40
|
- ✅ Very low and lightweight dependencies, can be run on any machine.
|
|
@@ -60,8 +61,25 @@ Optionally, scan a specific category or single module:
|
|
|
60
61
|
user-scanner -u <username> -c dev
|
|
61
62
|
user-scanner -l # Lists all available modules
|
|
62
63
|
user-scanner -u <username> -m github
|
|
64
|
+
user-scanner -u <username> -p <suffix>
|
|
63
65
|
|
|
64
66
|
```
|
|
67
|
+
|
|
68
|
+
Generate multiple username variations by appending a suffix:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
user-scanner -u <username> -p <suffix>
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
Optionally, scan a specific category or single module with limit:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
user-scanner -u <username> -p <suffix> -c dev
|
|
78
|
+
user-scanner -u <username> -p <suffix> -m github
|
|
79
|
+
user-scanner -u <username> -p <suffix> -s <number> # limit generation of usernames
|
|
80
|
+
user-scanner -u <username> -p <suffix> -d <seconds> #delay to avoid rate-limits
|
|
81
|
+
```
|
|
82
|
+
|
|
65
83
|
---
|
|
66
84
|
### Screenshot:
|
|
67
85
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
user_scanner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
user_scanner/__main__.py,sha256=
|
|
3
|
-
user_scanner/version.json,sha256=
|
|
2
|
+
user_scanner/__main__.py,sha256=r_8gq8wSRs3U7yUkTAv4e4MPopX0MUf_EbwedBTPs44,5082
|
|
3
|
+
user_scanner/version.json,sha256=JQUpDKvzaDU5eQcWcLXR7UejxgZkZcpBBYN1lgNmI6o,49
|
|
4
4
|
user_scanner/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
user_scanner/cli/banner.py,sha256=3t6owaDArERlvpcszA1Yi3dtksvh8a9tLyrxRowTC40,1499
|
|
6
6
|
user_scanner/community/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
user_scanner/community/coderlegion.py,sha256=W_bdjzdFPRgUrNFFlylvToSJ4AzaFCtTsUy_MRVDdSo,451
|
|
8
8
|
user_scanner/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
user_scanner/core/orchestrator.py,sha256=
|
|
9
|
+
user_scanner/core/orchestrator.py,sha256=mN8aeFxgIZaWuDKbjgYLmFPtOwe8TtF2bQtAgcR7nb8,5682
|
|
10
10
|
user_scanner/creator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
user_scanner/creator/devto.py,sha256=mIACmG1a4eoctywxb5p04sI0YVi3dsjCRw9YVOFBEKQ,435
|
|
12
12
|
user_scanner/creator/hashnode.py,sha256=LBg8yRkB-WAVYavfeKWh6R2LWmUHMyXt02BLIXP-C4o,1378
|
|
@@ -48,8 +48,8 @@ user_scanner/social/x.py,sha256=5UYBLmLuFpFeKQYqCkebQKB46GsfiQjV4flAdcrJAps,1591
|
|
|
48
48
|
user_scanner/social/youtube.py,sha256=nqW1XKZOefpPTDeCvqFTJXP24SjhntIQRlevwuSGLSk,2027
|
|
49
49
|
user_scanner/utils/update.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
50
|
user_scanner/utils/version.py,sha256=mPh24EwITyXgD3AMgbflRL180pS0JfrvuJdnoErOU34,623
|
|
51
|
-
user_scanner-1.0.
|
|
52
|
-
user_scanner-1.0.
|
|
53
|
-
user_scanner-1.0.
|
|
54
|
-
user_scanner-1.0.
|
|
55
|
-
user_scanner-1.0.
|
|
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
|