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 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
- CATEGORY_MAPPING = {
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
- from user_scanner import dev, social, creator, community, gaming, donation
20
- packages = {
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
- package = packages[cat_name]
33
- modules = load_modules(package)
16
+ path = categories[cat_name]
17
+ modules = load_modules(path)
34
18
  print(Fore.MAGENTA +
35
- f"\n== {cat_name.upper()} SITES =={Style.RESET_ALL}")
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=CATEGORY_MAPPING.keys(),
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
- if not args.username:
82
- parser.print_help()
83
- return
84
- else:
85
- print_banner()
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 package in packages:
94
- modules = load_modules(package)
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
- run_module_single(module, args.username)
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 = eval(CATEGORY_MAPPING[args.category])
118
+ category_package = load_categories().get(args.category)
107
119
  from user_scanner.core.orchestrator import run_checks_category
108
- run_checks_category(category_package, args.username, args.verbose)
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
- run_checks(args.username)
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 load_modules(package):
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 _, name, _ in pkgutil.iter_modules(package.__path__, package.__name__ + "."):
18
- try:
19
- module = importlib.import_module(name)
20
- modules.append(module)
21
- except Exception as e:
22
- print(f"Failed to import {name}: {e}")
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(package, username, verbose=False):
82
+ def run_checks_category(category_path:Path, username:str, verbose=False):
66
83
  global print_queue
67
84
 
68
- modules = load_modules(package)
69
- category_name = package.__name__.split('.')[-1].capitalize()
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 package in packages:
92
- run_checks_category(package, username)
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,4 +1,4 @@
1
1
  {
2
- "version": "1.0.6.0",
2
+ "version": "1.0.7.0",
3
3
  "version_type": "pypi"
4
4
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: user-scanner
3
- Version: 1.0.6.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
  ![1000136215](https://github.com/user-attachments/assets/49ec8d24-665b-4115-8525-01a8d0ca2ef4)
17
17
  <p align="center">
18
- <img src="https://img.shields.io/badge/Version-1.0.6.0-blueviolet?style=for-the-badge&logo=github" />
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=gKu_uqcydd5-9fZta2uL5r69i_BiQgzN43XqHGJyqLQ,3935
3
- user_scanner/version.json,sha256=55qDhyK3TTdk4-Ht586r_b7p7U6Ptdbapl1izhEzTaw,49
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=8kaokGxKo8uVemv7KCdrCQyjTXCZh1QrqSTurnlWLqY,4593
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.6.0.dist-info/entry_points.txt,sha256=XqU3kssYZ0vXaPy5qYUOTCu4u-48Xie7QWFpBCYc7Nc,59
52
- user_scanner-1.0.6.0.dist-info/licenses/LICENSE,sha256=XH1QyQG68zo1opDIZHTHcTAbe9XMzewvTaFTukcN9vc,1061
53
- user_scanner-1.0.6.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
54
- user_scanner-1.0.6.0.dist-info/METADATA,sha256=J5aHMpDhRHeJX2BP-gzKi3NafuE7ArjUb-kvLOlaILU,4185
55
- user_scanner-1.0.6.0.dist-info/RECORD,,
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,,