user-scanner 1.0.10.2__py3-none-any.whl → 1.1.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 +241 -129
- user_scanner/core/email_orchestrator.py +78 -0
- user_scanner/core/formatter.py +27 -0
- user_scanner/core/helpers.py +194 -2
- user_scanner/core/orchestrator.py +24 -112
- user_scanner/core/result.py +38 -13
- user_scanner/email_scan/adult/pornhub.py +62 -0
- user_scanner/email_scan/adult/xnxx.py +46 -0
- user_scanner/email_scan/adult/xvideos.py +50 -0
- user_scanner/email_scan/dev/.ruff_cache/.gitignore +2 -0
- user_scanner/email_scan/dev/.ruff_cache/0.14.10/10328336453267387919 +0 -0
- user_scanner/email_scan/dev/.ruff_cache/CACHEDIR.TAG +1 -0
- user_scanner/email_scan/dev/bitbucket.py +33 -0
- user_scanner/email_scan/dev/github.py +72 -0
- user_scanner/email_scan/dev/huggingface.py +37 -0
- user_scanner/email_scan/shopping/__init__.py +0 -0
- user_scanner/email_scan/shopping/ebay.py.lock +97 -0
- user_scanner/email_scan/shopping/flipkart.py +52 -0
- user_scanner/email_scan/social/__init__.py +0 -0
- user_scanner/email_scan/social/facebook.py +96 -0
- user_scanner/email_scan/social/instagram.py +48 -0
- user_scanner/email_scan/social/mastodon.py +57 -0
- user_scanner/email_scan/social/x.py +41 -0
- user_scanner/user_scan/community/lemmy.py +30 -0
- user_scanner/user_scan/creator/__init__.py +0 -0
- user_scanner/user_scan/creator/gumroad.py +22 -0
- user_scanner/{creator → user_scan/creator}/producthunt.py +13 -4
- user_scanner/user_scan/donation/__init__.py +0 -0
- user_scanner/user_scan/gaming/__init__.py +0 -0
- user_scanner/{gaming → user_scan/gaming}/roblox.py +15 -5
- user_scanner/version.json +1 -1
- user_scanner-1.1.0.dist-info/METADATA +239 -0
- user_scanner-1.1.0.dist-info/RECORD +94 -0
- user_scanner/cli/printer.py +0 -117
- user_scanner/config.json +0 -1
- user_scanner-1.0.10.2.dist-info/METADATA +0 -172
- user_scanner-1.0.10.2.dist-info/RECORD +0 -72
- /user_scanner/{creator → email_scan}/__init__.py +0 -0
- /user_scanner/{donation → email_scan/adult}/__init__.py +0 -0
- /user_scanner/{gaming → email_scan/dev}/__init__.py +0 -0
- /user_scanner/{community → user_scan/community}/__init__.py +0 -0
- /user_scanner/{community → user_scan/community}/coderlegion.py +0 -0
- /user_scanner/{community → user_scan/community}/hackernews.py +0 -0
- /user_scanner/{community → user_scan/community}/stackoverflow.py +0 -0
- /user_scanner/{creator → user_scan/creator}/devto.py +0 -0
- /user_scanner/{creator → user_scan/creator}/hashnode.py +0 -0
- /user_scanner/{creator → user_scan/creator}/itch_io.py +0 -0
- /user_scanner/{creator → user_scan/creator}/kaggle.py +0 -0
- /user_scanner/{creator → user_scan/creator}/medium.py +0 -0
- /user_scanner/{creator → user_scan/creator}/patreon.py +0 -0
- /user_scanner/{creator → user_scan/creator}/substack.py +0 -0
- /user_scanner/{creator → user_scan/creator}/twitch.py +0 -0
- /user_scanner/{dev → user_scan/dev}/__init__.py +0 -0
- /user_scanner/{dev → user_scan/dev}/bitbucket.py +0 -0
- /user_scanner/{dev → user_scan/dev}/codeberg.py +0 -0
- /user_scanner/{dev → user_scan/dev}/cratesio.py +0 -0
- /user_scanner/{dev → user_scan/dev}/dockerhub.py +0 -0
- /user_scanner/{dev → user_scan/dev}/github.py +0 -0
- /user_scanner/{dev → user_scan/dev}/gitlab.py +0 -0
- /user_scanner/{dev → user_scan/dev}/huggingface.py +0 -0
- /user_scanner/{dev → user_scan/dev}/launchpad.py +0 -0
- /user_scanner/{dev → user_scan/dev}/leetcode.py +0 -0
- /user_scanner/{dev → user_scan/dev}/npmjs.py +0 -0
- /user_scanner/{dev → user_scan/dev}/replit.py +0 -0
- /user_scanner/{dev → user_scan/dev}/sourceforge.py +0 -0
- /user_scanner/{donation → user_scan/donation}/buymeacoffee.py +0 -0
- /user_scanner/{donation → user_scan/donation}/liberapay.py +0 -0
- /user_scanner/{gaming → user_scan/gaming}/chess_com.py +0 -0
- /user_scanner/{gaming → user_scan/gaming}/lichess.py +0 -0
- /user_scanner/{gaming → user_scan/gaming}/minecraft.py +0 -0
- /user_scanner/{gaming → user_scan/gaming}/monkeytype.py +0 -0
- /user_scanner/{gaming → user_scan/gaming}/osu.py +0 -0
- /user_scanner/{gaming → user_scan/gaming}/steam.py +0 -0
- /user_scanner/{social → user_scan/social}/__init__.py +0 -0
- /user_scanner/{social → user_scan/social}/bluesky.py +0 -0
- /user_scanner/{social → user_scan/social}/discord.py +0 -0
- /user_scanner/{social → user_scan/social}/instagram.py +0 -0
- /user_scanner/{social → user_scan/social}/mastodon.py +0 -0
- /user_scanner/{social → user_scan/social}/pinterest.py +0 -0
- /user_scanner/{social → user_scan/social}/reddit.py +0 -0
- /user_scanner/{social → user_scan/social}/snapchat.py +0 -0
- /user_scanner/{social → user_scan/social}/soundcloud.py +0 -0
- /user_scanner/{social → user_scan/social}/telegram.py +0 -0
- /user_scanner/{social → user_scan/social}/threads.py +0 -0
- /user_scanner/{social → user_scan/social}/tiktok.py +0 -0
- /user_scanner/{social → user_scan/social}/x.py +0 -0
- /user_scanner/{social → user_scan/social}/youtube.py +0 -0
- {user_scanner-1.0.10.2.dist-info → user_scanner-1.1.0.dist-info}/WHEEL +0 -0
- {user_scanner-1.0.10.2.dist-info → user_scanner-1.1.0.dist-info}/entry_points.txt +0 -0
- {user_scanner-1.0.10.2.dist-info → user_scanner-1.1.0.dist-info}/licenses/LICENSE +0 -0
user_scanner/__main__.py
CHANGED
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import time
|
|
3
3
|
import sys
|
|
4
|
-
|
|
5
|
-
from user_scanner.core.orchestrator import generate_permutations, load_categories
|
|
4
|
+
import re
|
|
6
5
|
from colorama import Fore, Style
|
|
6
|
+
|
|
7
7
|
from user_scanner.cli.banner import print_banner
|
|
8
|
-
from
|
|
9
|
-
from user_scanner.core
|
|
10
|
-
from user_scanner.core.helpers import is_last_value
|
|
8
|
+
from user_scanner.core.version import load_local_version
|
|
9
|
+
from user_scanner.core import formatter
|
|
11
10
|
from user_scanner.utils.updater_logic import check_for_updates
|
|
12
11
|
from user_scanner.utils.update import update_self
|
|
13
12
|
|
|
13
|
+
from user_scanner.core.helpers import (
|
|
14
|
+
load_categories,
|
|
15
|
+
load_modules,
|
|
16
|
+
find_module,
|
|
17
|
+
get_site_name,
|
|
18
|
+
generate_permutations,
|
|
19
|
+
set_proxy_manager,
|
|
20
|
+
get_proxy_count
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from user_scanner.core.orchestrator import (
|
|
24
|
+
run_user_full,
|
|
25
|
+
run_user_category,
|
|
26
|
+
run_user_module
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from user_scanner.core.email_orchestrator import (
|
|
30
|
+
run_email_full_batch,
|
|
31
|
+
run_email_category_batch,
|
|
32
|
+
run_email_module_batch
|
|
33
|
+
)
|
|
34
|
+
|
|
14
35
|
# Color configs
|
|
15
36
|
R = Fore.RED
|
|
16
37
|
G = Fore.GREEN
|
|
@@ -18,163 +39,254 @@ C = Fore.CYAN
|
|
|
18
39
|
Y = Fore.YELLOW
|
|
19
40
|
X = Fore.RESET
|
|
20
41
|
|
|
21
|
-
|
|
22
|
-
MAX_PERMUTATIONS_LIMIT = 100 # To prevent excessive generation
|
|
42
|
+
MAX_PERMUTATIONS_LIMIT = 100
|
|
23
43
|
|
|
24
44
|
|
|
25
45
|
def main():
|
|
26
|
-
|
|
27
46
|
parser = argparse.ArgumentParser(
|
|
28
47
|
prog="user-scanner",
|
|
29
|
-
description="Scan usernames across multiple platforms."
|
|
30
|
-
)
|
|
31
|
-
parser.add_argument(
|
|
32
|
-
"-u", "--username", help="Username to scan across platforms"
|
|
33
|
-
)
|
|
34
|
-
parser.add_argument(
|
|
35
|
-
"-c", "--category", choices=load_categories().keys(),
|
|
36
|
-
help="Scan all platforms in a category"
|
|
37
|
-
)
|
|
38
|
-
parser.add_argument(
|
|
39
|
-
"-m", "--module", help="Scan a single specific module across all categories"
|
|
40
|
-
)
|
|
41
|
-
parser.add_argument(
|
|
42
|
-
"-l", "--list", action="store_true", help="List all available modules by category"
|
|
43
|
-
)
|
|
44
|
-
parser.add_argument(
|
|
45
|
-
"-v", "--verbose", action="store_true", help="Enable verbose output"
|
|
48
|
+
description="Scan usernames or emails across multiple platforms."
|
|
46
49
|
)
|
|
47
50
|
|
|
48
|
-
parser.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
group = parser.add_mutually_exclusive_group(required=False)
|
|
52
|
+
|
|
53
|
+
group.add_argument("-u", "--username",
|
|
54
|
+
help="Username to scan across platforms")
|
|
55
|
+
group.add_argument("-e", "--email", help="Email to scan across platforms")
|
|
56
|
+
|
|
57
|
+
group.add_argument("-uf", "--username-file",
|
|
58
|
+
help="File containing usernames (one per line)")
|
|
59
|
+
group.add_argument("-ef", "--email-file",
|
|
60
|
+
help="File containing emails (one per line)")
|
|
61
|
+
|
|
62
|
+
parser.add_argument("-c", "--category",
|
|
63
|
+
help="Scan all platforms in a category")
|
|
64
|
+
|
|
65
|
+
parser.add_argument("-m", "--module", help="Scan a single specific module")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
parser.add_argument("-lu", "--list-user", action="store_true",
|
|
69
|
+
help="List all available modules for username scanning")
|
|
70
|
+
|
|
71
|
+
parser.add_argument("-le", "--list-email", action="store_true",
|
|
72
|
+
help="List all available modules for email scanning")
|
|
73
|
+
|
|
74
|
+
parser.add_argument("-v", "--verbose", action="store_true",
|
|
75
|
+
help="Enable verbose output")
|
|
76
|
+
|
|
77
|
+
parser.add_argument("-p", "--permute", type=str,
|
|
78
|
+
help="Generate permutations using a pattern")
|
|
79
|
+
|
|
80
|
+
parser.add_argument("-s", "--stop", type=int,
|
|
81
|
+
default=MAX_PERMUTATIONS_LIMIT, help="Limit permutations")
|
|
82
|
+
|
|
83
|
+
parser.add_argument("-d", "--delay", type=float,
|
|
84
|
+
default=0, help="Delay between requests")
|
|
54
85
|
|
|
55
86
|
parser.add_argument(
|
|
56
|
-
"-
|
|
57
|
-
|
|
87
|
+
"-f", "--format", choices=["csv", "json"], help="Output format")
|
|
88
|
+
|
|
89
|
+
parser.add_argument("-o", "--output", type=str, help="Output file path")
|
|
58
90
|
|
|
59
91
|
parser.add_argument(
|
|
60
|
-
"-
|
|
61
|
-
)
|
|
92
|
+
"-P", "--proxy-file", type=str, help="Path to proxy list file (one proxy per line)")
|
|
62
93
|
|
|
63
94
|
parser.add_argument(
|
|
64
|
-
"-
|
|
65
|
-
|
|
95
|
+
"--validate-proxies", action="store_true",
|
|
96
|
+
help="Validate proxies before scanning (tests against google.com)")
|
|
97
|
+
|
|
66
98
|
parser.add_argument(
|
|
67
|
-
"-U", "--update", action="store_true",
|
|
68
|
-
)
|
|
99
|
+
"-U", "--update", action="store_true", help="Update the tool")
|
|
69
100
|
|
|
70
|
-
|
|
101
|
+
parser.add_argument("--version", action="store_true", help="Print version")
|
|
71
102
|
|
|
72
|
-
|
|
103
|
+
args = parser.parse_args()
|
|
73
104
|
|
|
74
|
-
if args.update
|
|
105
|
+
if args.update:
|
|
75
106
|
update_self()
|
|
76
107
|
print(f"[{G}+{X}] {G}Update successful. Please restart the tool.{X}")
|
|
77
108
|
sys.exit(0)
|
|
78
109
|
|
|
79
|
-
if args.
|
|
80
|
-
|
|
110
|
+
if args.version:
|
|
111
|
+
version, _ = load_local_version()
|
|
112
|
+
print(f"user-scanner current version -> {G}{version}{X}")
|
|
113
|
+
sys.exit(0)
|
|
114
|
+
|
|
115
|
+
if args.list_user:
|
|
116
|
+
categories = load_categories()
|
|
117
|
+
for cat_name, cat_path in categories.items():
|
|
118
|
+
modules = load_modules(cat_path)
|
|
119
|
+
print(Fore.MAGENTA +
|
|
120
|
+
f"\n== {cat_name.upper()} SITES =={Style.RESET_ALL}")
|
|
121
|
+
for module in modules:
|
|
122
|
+
print(f" - {get_site_name(module)}")
|
|
81
123
|
return
|
|
82
124
|
|
|
83
|
-
|
|
125
|
+
if args.list_email:
|
|
126
|
+
categories = load_categories(is_email=True)
|
|
127
|
+
for cat_name, cat_path in categories.items():
|
|
128
|
+
modules = load_modules(cat_path)
|
|
129
|
+
print(Fore.MAGENTA +
|
|
130
|
+
f"\n== {cat_name.upper()} SITES =={Style.RESET_ALL}")
|
|
131
|
+
for module in modules:
|
|
132
|
+
print(f" - {get_site_name(module)}")
|
|
133
|
+
return
|
|
84
134
|
|
|
85
|
-
if not args.username:
|
|
135
|
+
if not (args.username or args.email or args.username_file or args.email_file):
|
|
86
136
|
parser.print_help()
|
|
87
137
|
return
|
|
88
138
|
|
|
89
|
-
|
|
90
|
-
if
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
is_last = i == len(usernames) - 1
|
|
121
|
-
if arg is None:
|
|
122
|
-
results.extend(func(name, Printer, is_last))
|
|
139
|
+
# Initialize proxy manager if proxy file is provided
|
|
140
|
+
if args.proxy_file:
|
|
141
|
+
try:
|
|
142
|
+
# Validate proxies if flag is set
|
|
143
|
+
if args.validate_proxies:
|
|
144
|
+
print(f"{C}[*] Validating proxies from {args.proxy_file}...{X}")
|
|
145
|
+
from user_scanner.core.helpers import validate_proxies, ProxyManager
|
|
146
|
+
|
|
147
|
+
# Load proxies first
|
|
148
|
+
temp_manager = ProxyManager(args.proxy_file)
|
|
149
|
+
all_proxies = temp_manager.proxies
|
|
150
|
+
print(f"{C}[*] Testing {len(all_proxies)} proxies...{X}")
|
|
151
|
+
|
|
152
|
+
# Validate them
|
|
153
|
+
working_proxies = validate_proxies(all_proxies)
|
|
154
|
+
|
|
155
|
+
if not working_proxies:
|
|
156
|
+
print(f"{R}[✘] No working proxies found{X}")
|
|
157
|
+
sys.exit(1)
|
|
158
|
+
|
|
159
|
+
print(f"{G}[+] Found {len(working_proxies)} working proxies out of {len(all_proxies)}{X}")
|
|
160
|
+
|
|
161
|
+
# Save working proxies to temp file
|
|
162
|
+
temp_proxy_file = "validated_proxies.txt"
|
|
163
|
+
with open(temp_proxy_file, 'w', encoding='utf-8') as f:
|
|
164
|
+
for proxy in working_proxies:
|
|
165
|
+
f.write(proxy + '\n')
|
|
166
|
+
|
|
167
|
+
set_proxy_manager(temp_proxy_file)
|
|
168
|
+
proxy_count = get_proxy_count()
|
|
169
|
+
print(f"{G}[+] Using {proxy_count} validated proxies{X}")
|
|
123
170
|
else:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
print(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
results = []
|
|
132
|
-
|
|
133
|
-
if args.module:
|
|
134
|
-
# Single module search across all categories
|
|
135
|
-
from user_scanner.core.orchestrator import run_module_single, find_module
|
|
136
|
-
modules = find_module(args.module)
|
|
137
|
-
|
|
138
|
-
if len(modules) > 0:
|
|
139
|
-
for module in modules:
|
|
140
|
-
results.extend(run_all_usernames(run_module_single, module))
|
|
141
|
-
else:
|
|
142
|
-
print(
|
|
143
|
-
R + f"[!] Module '{args.module}' not found in any category." + Style.RESET_ALL)
|
|
144
|
-
|
|
145
|
-
elif args.category:
|
|
146
|
-
# Category-wise scan
|
|
147
|
-
category_package = load_categories().get(args.category)
|
|
148
|
-
from user_scanner.core.orchestrator import run_checks_category
|
|
149
|
-
results = run_all_usernames(run_checks_category, category_package)
|
|
171
|
+
set_proxy_manager(args.proxy_file)
|
|
172
|
+
proxy_count = get_proxy_count()
|
|
173
|
+
print(f"{G}[+] Loaded {proxy_count} proxies from {args.proxy_file}{X}")
|
|
174
|
+
except Exception as e:
|
|
175
|
+
print(f"{R}[✘] Error loading proxies: {e}{X}")
|
|
176
|
+
sys.exit(1)
|
|
150
177
|
|
|
178
|
+
check_for_updates()
|
|
179
|
+
print_banner()
|
|
180
|
+
|
|
181
|
+
# Handle bulk email file
|
|
182
|
+
if args.email_file:
|
|
183
|
+
try:
|
|
184
|
+
with open(args.email_file, 'r', encoding='utf-8') as f:
|
|
185
|
+
emails = [line.strip() for line in f if line.strip() and not line.startswith('#')]
|
|
186
|
+
|
|
187
|
+
# Validate email formats
|
|
188
|
+
valid_emails = []
|
|
189
|
+
for email in emails:
|
|
190
|
+
if re.findall(r"^[^@\s]+@[^@\s]+\.[^@\s]+$", email):
|
|
191
|
+
valid_emails.append(email)
|
|
192
|
+
else:
|
|
193
|
+
print(f"{Y}[!] Skipping invalid email format: {email}{X}")
|
|
194
|
+
|
|
195
|
+
if not valid_emails:
|
|
196
|
+
print(f"{R}[✘] Error: No valid emails found in {args.email_file}{X}")
|
|
197
|
+
sys.exit(1)
|
|
198
|
+
|
|
199
|
+
print(f"{C}[+] Loaded {len(valid_emails)} emails from {args.email_file}{X}")
|
|
200
|
+
is_email = True
|
|
201
|
+
targets = valid_emails
|
|
202
|
+
except FileNotFoundError:
|
|
203
|
+
print(f"{R}[✘] Error: File not found: {args.email_file}{X}")
|
|
204
|
+
sys.exit(1)
|
|
205
|
+
except Exception as e:
|
|
206
|
+
print(f"{R}[✘] Error reading email file: {e}{X}")
|
|
207
|
+
sys.exit(1)
|
|
208
|
+
# Handle bulk username file
|
|
209
|
+
elif args.username_file:
|
|
210
|
+
try:
|
|
211
|
+
with open(args.username_file, 'r', encoding='utf-8') as f:
|
|
212
|
+
usernames = [line.strip() for line in f if line.strip() and not line.startswith('#')]
|
|
213
|
+
if not usernames:
|
|
214
|
+
print(f"{R}[✘] Error: No valid usernames found in {args.username_file}{X}")
|
|
215
|
+
sys.exit(1)
|
|
216
|
+
print(f"{C}[+] Loaded {len(usernames)} usernames from {args.username_file}{X}")
|
|
217
|
+
is_email = False
|
|
218
|
+
targets = usernames
|
|
219
|
+
except FileNotFoundError:
|
|
220
|
+
print(f"{R}[✘] Error: File not found: {args.username_file}{X}")
|
|
221
|
+
sys.exit(1)
|
|
222
|
+
except Exception as e:
|
|
223
|
+
print(f"{R}[✘] Error reading username file: {e}{X}")
|
|
224
|
+
sys.exit(1)
|
|
151
225
|
else:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
226
|
+
is_email = args.email is not None
|
|
227
|
+
if is_email and not re.findall(r"^[^@\s]+@[^@\s]+\.[^@\s]+$", args.email):
|
|
228
|
+
print(R + "[✘] Error: Invalid email format." + X)
|
|
229
|
+
sys.exit(1)
|
|
230
|
+
|
|
231
|
+
target_name = args.username or args.email
|
|
232
|
+
targets = [target_name]
|
|
233
|
+
|
|
234
|
+
# Handle permutations (only for single username/email)
|
|
235
|
+
if args.permute and not (args.username_file or args.email_file):
|
|
236
|
+
target_name = args.username or args.email
|
|
237
|
+
targets = generate_permutations(
|
|
238
|
+
target_name, args.permute, args.stop, is_email)
|
|
239
|
+
print(
|
|
240
|
+
C + f"[+] Generated {len(targets)} permutations" + Style.RESET_ALL)
|
|
241
|
+
elif args.permute and (args.username_file or args.email_file):
|
|
242
|
+
print(f"{R}[✘] Error: Permutations not supported with file-based scanning{X}")
|
|
243
|
+
sys.exit(1)
|
|
168
244
|
|
|
169
|
-
|
|
170
|
-
char = "" if Printer.is_csv or is_last_value(results, i) else ","
|
|
171
|
-
content += "\n" + Printer.get_result_output(result) + char
|
|
245
|
+
results = []
|
|
172
246
|
|
|
173
|
-
|
|
174
|
-
|
|
247
|
+
for i, target in enumerate(targets):
|
|
248
|
+
if i != 0 and args.delay:
|
|
249
|
+
time.sleep(args.delay)
|
|
175
250
|
|
|
176
|
-
|
|
177
|
-
|
|
251
|
+
if is_email:
|
|
252
|
+
print(f"\n{Fore.CYAN} Checking email: {target}{Style.RESET_ALL}")
|
|
253
|
+
else:
|
|
254
|
+
print(f"\n{Fore.CYAN} Checking username: {target}{Style.RESET_ALL}")
|
|
255
|
+
|
|
256
|
+
if args.module:
|
|
257
|
+
modules = find_module(args.module, is_email)
|
|
258
|
+
fn = run_email_module_batch if is_email else run_user_module
|
|
259
|
+
if modules:
|
|
260
|
+
for module in modules:
|
|
261
|
+
results.extend(fn(module, target))
|
|
262
|
+
else:
|
|
263
|
+
print(
|
|
264
|
+
R +
|
|
265
|
+
f"[!] {'Email' if is_email else 'User'} module '{args.module}' not found." +
|
|
266
|
+
Style.RESET_ALL
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
elif args.category:
|
|
270
|
+
cat_path = load_categories(is_email).get(args.category)
|
|
271
|
+
fn = run_email_category_batch if is_email else run_user_category
|
|
272
|
+
if cat_path:
|
|
273
|
+
results.extend(fn(cat_path, target))
|
|
274
|
+
else:
|
|
275
|
+
print(
|
|
276
|
+
R +
|
|
277
|
+
f"[!] {'Email' if is_email else 'User'} category '{args.module}' not found." +
|
|
278
|
+
Style.RESET_ALL
|
|
279
|
+
)
|
|
280
|
+
else:
|
|
281
|
+
fn = run_email_full_batch if is_email else run_user_full
|
|
282
|
+
results.extend(fn(target))
|
|
283
|
+
|
|
284
|
+
if args.output:
|
|
285
|
+
content = formatter.into_csv(
|
|
286
|
+
results) if args.format == "csv" else formatter.into_json(results)
|
|
287
|
+
with open(args.output, "a", encoding="utf-8") as f:
|
|
288
|
+
f.write(content)
|
|
289
|
+
print(G + f"\n[+] Results saved to {args.output}" + Style.RESET_ALL)
|
|
178
290
|
|
|
179
291
|
|
|
180
292
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import List
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
from colorama import Fore, Style
|
|
6
|
+
|
|
7
|
+
from user_scanner.core.helpers import load_categories, load_modules, find_category
|
|
8
|
+
from user_scanner.core.result import Result
|
|
9
|
+
|
|
10
|
+
# Concurrency control
|
|
11
|
+
MAX_CONCURRENT_REQUESTS = 15
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def _async_worker(module: ModuleType, email: str, sem: asyncio.Semaphore) -> Result:
|
|
15
|
+
async with sem:
|
|
16
|
+
module_name = module.__name__.split('.')[-1]
|
|
17
|
+
func_name = f"validate_{module_name}"
|
|
18
|
+
|
|
19
|
+
if not hasattr(module, func_name):
|
|
20
|
+
return Result.error(f"Function {func_name} not found")
|
|
21
|
+
|
|
22
|
+
func = getattr(module, func_name)
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
res = func(email)
|
|
26
|
+
result = await res if asyncio.iscoroutine(res) else res
|
|
27
|
+
except Exception as e:
|
|
28
|
+
result = Result.error(e)
|
|
29
|
+
|
|
30
|
+
# Use helper to get actual dir name for the Result object
|
|
31
|
+
actual_cat = find_category(module) or "Email"
|
|
32
|
+
|
|
33
|
+
result.update(
|
|
34
|
+
site_name=module_name.capitalize(),
|
|
35
|
+
username=email,
|
|
36
|
+
category=actual_cat,
|
|
37
|
+
is_email=True
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
print(result.get_console_output())
|
|
41
|
+
return result
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def _run_batch(modules: List[ModuleType], email:str) -> List[Result]:
|
|
45
|
+
sem = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
|
|
46
|
+
tasks = []
|
|
47
|
+
for module in modules:
|
|
48
|
+
tasks.append(_async_worker(module, email, sem))
|
|
49
|
+
|
|
50
|
+
if not tasks:
|
|
51
|
+
return []
|
|
52
|
+
return list(await asyncio.gather(*tasks))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def run_email_module_batch(module: ModuleType, email: str) -> List[Result]:
|
|
56
|
+
return asyncio.run(_run_batch([module], email))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def run_email_category_batch(category_path: Path, email: str) -> List[Result]:
|
|
60
|
+
cat_name = category_path.stem.capitalize()
|
|
61
|
+
print(f"\n{Fore.MAGENTA}== {cat_name} SITES =={Style.RESET_ALL}")
|
|
62
|
+
|
|
63
|
+
modules = load_modules(category_path)
|
|
64
|
+
return asyncio.run(_run_batch(modules, email))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def run_email_full_batch(email: str) -> List[Result]:
|
|
68
|
+
categories = load_categories(is_email=True)
|
|
69
|
+
all_results = []
|
|
70
|
+
|
|
71
|
+
for cat_name, cat_path in categories.items():
|
|
72
|
+
print(f"\n{Fore.MAGENTA}== {cat_name.upper()} SITES =={Style.RESET_ALL}")
|
|
73
|
+
|
|
74
|
+
modules = load_modules(cat_path)
|
|
75
|
+
cat_results = asyncio.run(_run_batch(modules, email))
|
|
76
|
+
all_results.extend(cat_results)
|
|
77
|
+
|
|
78
|
+
return all_results
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from user_scanner.core.result import Result
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
INDENT = " "
|
|
5
|
+
CSV_HEADER = "username,category,site_name,status,reason"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def indentate(msg: str, indent: int):
|
|
9
|
+
if indent <= 0:
|
|
10
|
+
return msg
|
|
11
|
+
tabs = INDENT * indent
|
|
12
|
+
return "\n".join([f"{tabs}{line}" for line in msg.split("\n")])
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def into_json(results: List[Result]) -> str:
|
|
16
|
+
res = "[\n"
|
|
17
|
+
|
|
18
|
+
for i, result in enumerate(results):
|
|
19
|
+
is_last = i == len(results) - 1
|
|
20
|
+
end = "" if is_last else ","
|
|
21
|
+
res += indentate(result.to_json().replace("\t", INDENT), 1) + end + "\n"
|
|
22
|
+
|
|
23
|
+
return res + "]"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def into_csv(results: List[Result]) -> str:
|
|
27
|
+
return CSV_HEADER + "\n" + "\n".join(result.to_csv() for result in results)
|