diff --git a/user_scanner/user_scan/creator/unsplash.py b/user_scanner/user_scan/creator/unsplash.py
new file mode 100644
index 00000000..ba141467
--- /dev/null
+++ b/user_scanner/user_scan/creator/unsplash.py
@@ -0,0 +1,39 @@
+import re
+import html
+import httpx
+from user_scanner.core.result import Result
+from user_scanner.core.orchestrator import make_request
+
+def validate_unsplash(username: str) -> Result:
+ """Validate a username on Unsplash."""
+ url = f"https://unsplash.com/@{username}"
+ show_url = f"https://unsplash.com/@{username}"
+
+ try:
+ # We pass headers={} to use httpx's default user-agent,
+ # as Unsplash blocks/redirects the default browser user-agent.
+ response = make_request(url, headers={}, follow_redirects=True)
+ response_text = response.text
+
+ # Verify status code 200 AND check unique Unsplash strings in the response body to prevent false positives
+ if response.status_code == 200 and "unsplash" in response_text.lower() and "page not found" not in response_text.lower():
+ extra = {}
+ title_match = re.search(r"
(.*?)", response_text, re.IGNORECASE)
+ if title_match:
+ title = html.unescape(title_match.group(1).strip())
+ title_clean = title.split(" | ")[0]
+ match = re.search(r"^(.*?)\s*\((?:@" + re.escape(username) + r")\)", title_clean, re.IGNORECASE)
+ if match:
+ name = match.group(1).strip()
+ name = re.sub(r"'\s*s (?:Collections|Photos|Likes|Stats)$", "", name, flags=re.IGNORECASE)
+ name = re.sub(r"'\''s (?:Collections|Photos|Likes|Stats)$", "", name, flags=re.IGNORECASE)
+ extra["name"] = name
+ return Result.taken(extra=extra, url=show_url)
+ elif response.status_code == 404 or "page not found" in response_text.lower():
+ return Result.available(url=show_url)
+ else:
+ return Result.error(f"Unexpected status: {response.status_code}", url=show_url)
+ except httpx.TimeoutException:
+ return Result.skipped("Connection timed out")
+ except Exception as e:
+ return Result.error(e, url=show_url)
diff --git a/user_scanner/user_scan/dev/hackerone.py b/user_scanner/user_scan/dev/hackerone.py
new file mode 100644
index 00000000..fc2ad116
--- /dev/null
+++ b/user_scanner/user_scan/dev/hackerone.py
@@ -0,0 +1,33 @@
+import re
+import html
+import httpx
+from user_scanner.core.result import Result
+from user_scanner.core import orchestrator
+
+def validate_hackerone(username: str) -> Result:
+ """Validate a username on HackerOne."""
+ url = f"https://hackerone.com/{username}"
+ show_url = f"https://hackerone.com/{username}"
+ try:
+ response = orchestrator.make_request(url, follow_redirects=True)
+ response_text = response.text
+
+ # Verify status code 200 AND check unique HackerOne strings in the response body to prevent false positives
+ if response.status_code == 200 and "hackerone" in response_text.lower() and f"hackerone.com/{username}" in response_text.lower():
+ extra = {}
+ # Extract user bio/description from OpenGraph metadata
+ bio_match = re.search(r'property="og:description"\s+content="([^"]+)"', response_text, re.IGNORECASE)
+ if not bio_match:
+ bio_match = re.search(r'content="([^"]+)"\s+property="og:description"', response_text, re.IGNORECASE)
+ if bio_match:
+ bio = html.unescape(bio_match.group(1).strip())
+ if bio and bio != "-":
+ extra["bio"] = bio
+ return Result.taken(extra=extra, url=show_url)
+ elif response.status_code == 404 or "page not found" in response_text.lower():
+ return Result.available(url=show_url)
+ return Result.error(f"Unexpected status: {response.status_code}", url=show_url)
+ except httpx.TimeoutException:
+ return Result.skipped("Connection timed out")
+ except Exception as e:
+ return Result.error(e, url=show_url)
diff --git a/user_scanner/user_scan/dev/wix.py b/user_scanner/user_scan/dev/wix.py
new file mode 100644
index 00000000..9201f10f
--- /dev/null
+++ b/user_scanner/user_scan/dev/wix.py
@@ -0,0 +1,31 @@
+import re
+import html
+import httpx
+from user_scanner.core.result import Result
+from user_scanner.core import orchestrator
+
+def validate_wix(username: str) -> Result:
+ """Validate a username on Wix."""
+ url = f"https://{username}.wix.com"
+ show_url = f"https://{username}.wix.com"
+ try:
+ response = orchestrator.make_request(url, follow_redirects=True)
+ response_text = response.text
+
+ # Verify status code 200 AND check unique Wix strings in the response body to prevent false positives
+ if response.status_code == 200 and "wix" in response_text.lower():
+ extra = {}
+ title_match = re.search(r"(.*?)", response_text, re.IGNORECASE)
+ if title_match:
+ extra["title"] = html.unescape(title_match.group(1).strip())
+ return Result.taken(extra=extra, url=show_url)
+ elif response.status_code == 404:
+ return Result.available(url=show_url)
+ return Result.error(f"Unexpected status: {response.status_code}", url=show_url)
+ except httpx.ConnectError:
+ # If the domain doesn't resolve at all, it usually means the username/subdomain is available
+ return Result.available(url=show_url)
+ except httpx.TimeoutException:
+ return Result.skipped("Connection timed out")
+ except Exception as e:
+ return Result.error(e, url=show_url)
diff --git a/user_scanner/user_scan/finance/tradingview.py b/user_scanner/user_scan/finance/tradingview.py
new file mode 100644
index 00000000..2a77e7cd
--- /dev/null
+++ b/user_scanner/user_scan/finance/tradingview.py
@@ -0,0 +1,35 @@
+import re
+import html
+import httpx
+from user_scanner.core.result import Result
+from user_scanner.core import orchestrator
+
+def validate_tradingview(username: str) -> Result:
+ """Validate a username on TradingView."""
+ url = f"https://www.tradingview.com/u/{username}/"
+ show_url = f"https://www.tradingview.com/u/{username}/"
+ try:
+ response = orchestrator.make_request(url, follow_redirects=True)
+ response_text = response.text
+
+ # Verify status code 200 AND check unique TradingView strings in the response body to prevent false positives
+ if response.status_code == 200 and "tradingview" in response_text.lower() and "page not found" not in response_text.lower():
+ extra = {}
+ title_match = re.search(r"(.*?)", response_text, re.IGNORECASE)
+ if title_match:
+ title = html.unescape(title_match.group(1).strip())
+ # TradingView format: {Name} — Trading Ideas and Scripts — TradingView
+ # or {Username} — Trading Ideas and Scripts — TradingView
+ title_parts = title.split(" — ")
+ if len(title_parts) > 0:
+ name = title_parts[0].strip()
+ if name.lower() != username.lower():
+ extra["name"] = name
+ return Result.taken(extra=extra, url=show_url)
+ elif response.status_code == 404 or "page not found" in response_text.lower():
+ return Result.available(url=show_url)
+ return Result.error(f"Unexpected status: {response.status_code}", url=show_url)
+ except httpx.TimeoutException:
+ return Result.skipped("Connection timed out")
+ except Exception as e:
+ return Result.error(e, url=show_url)