Username Scanning Module Function Signature in user-scanner
A username scanning module must expose exactly one validator function named validate_<sitename> that accepts a username string and returns a Result object indicating availability, taken status with metadata, or an error.
The kaifcodec/user-scanner repository enforces a strict contract for all username scanning modules. This guide walks through the expected function signature, return type requirements, and implementation patterns derived directly from the source code.
The Required Function Signature
Every username scanning module must implement a single validator function following this exact pattern:
def validate_<sitename>(user: str) -> Result:
...
The naming convention is mandatory: prefix validate_ followed by the lowercase site identifier. For example, validate_github, validate_twitter, or validate_linkedin.
Parameter and Return Specifications
| Element | Specification |
|---|---|
Parameter user |
Raw username string supplied by the caller |
| Return type | Instance of user_scanner.core.result.Result |
| Exception handling | No exceptions allowed — all errors must return via Result.error() |
The Result Object: Three Factory Methods
The Result class in user_scanner/core/result.py provides three static factory methods for constructing return values:
Result.available()— The username does not exist on the platformResult.taken(extra={...}, media={...})— The username exists; optionalextradict holds structured metadata (fullname, location, bio), andmediadict contains image URLsResult.error("message")— Any unexpected condition: network failures, rate limits, or parsing errors
Error Handling Rule
Validators must not raise exceptions. The following pattern is forbidden:
# INCORRECT — do not use
raise ValueError("unexpected response")
return Result.error("unexpected response") # CORRECT
Implementation Patterns from core/orchestrator.py
Most modules leverage helper utilities to maintain concise, DRY code. The two primary helpers are defined in user_scanner/core/orchestrator.py:
generic_validate— Standard HTTP validation with configurable request parametersimpersonate_validate— For bot-protected sites requiring browser impersonation
Minimal Validator Example (GitHub)
from user_scanner.core.orchestrator import generic_validate
from user_scanner.core.result import Result
import re
def validate_github(user: str) -> Result:
"""Check whether a GitHub username exists."""
url = f"https://github.com/{user}"
show_url = f"https://github.com/{user}"
def process(resp):
# Username available: 404 or explicit "Not Found"
if resp.status_code == 404 or "Not Found" in resp.text:
return Result.available()
# Username taken: profile header present
if resp.status_code == 200 and 'class="vcard-fullname"' in resp.text:
extra = {}
m = re.search(r'class="vcard-fullname">([^<]+)</', resp.text)
if m:
extra["fullname"] = m.group(1).strip()
return Result.taken(
extra=extra,
media={"avatar": f"https://github.com/{user}.png"}
)
# Unexpected state
return Result.error(f"Unexpected status {resp.status_code}")
return generic_validate(url, process, show_url=show_url, follow_redirects=True)
Bot-Protected Site Pattern (Snapchat)
For platforms protected by Cloudflare or similar services, use impersonate_validate from user_scanner/core/impersonate.py:
from user_scanner.core.impersonate import impersonate_validate
from user_scanner.core.result import Result
def validate_snapchat(user: str) -> Result:
"""Validate Snapchat usernames behind Cloudflare protection."""
url = f"https://www.snapchat.com/add/{user}"
def process(resp):
if resp.status_code == 404 and "Sorry, we couldn't find" in resp.text:
return Result.available()
if resp.status_code == 200 and "profile-card" in resp.text:
return Result.taken()
return Result.error(f"Unexpected status {resp.status_code}")
return impersonate_validate(
url,
process,
warmup_url="https://www.snapchat.com/",
impersonate="chrome",
show_url=url,
)
Critical Implementation Requirements
According to the contributing guidelines in CONTRIBUTING.md, every username scanning module must satisfy these five rules:
- Exact naming — Function name must be
validate_<sitename>with matching lowercase filename - Explicit verification — Never rely solely on HTTP 200; validators must identify unique markers confirming both taken and available states
- Metadata extraction — Include rich structured data in
Result.taken()when profiles are found - Error containment — Return
Result.error()for all failure paths; exceptions break the orchestration - Safe URL construction — Use
paramsarguments rather than f-strings for user-controlled data in request parameters
Key Source Files
| Purpose | Path |
|---|---|
| Contribution guidelines — signature definition | CONTRIBUTING.md |
Result class implementation |
user_scanner/core/result.py |
Validation helpers (generic_validate) |
user_scanner/core/orchestrator.py |
| Browser impersonation helper | user_scanner/core/impersonate.py |
| Reference implementation (Reddit) | user_scanner/user_scan/social/reddit.py |
| Async pattern reference (Mastodon email) | user_scanner/email_scan/social/mastodon.py |
Summary
- Function signature:
def validate_<sitename>(user: str) -> Result - Return exclusively via
Result.available(),Result.taken(), orResult.error() - Never raise exceptions — express all errors through
Result.error() - Use helpers from
core/orchestrator.pyandcore/impersonate.pyfor clean implementations - Follow naming conventions exactly: filename matches
validate_prefix target
Frequently Asked Questions
What happens if a validator raises an exception instead of returning Result.error()?
The orchestration system in user_scanner/core/orchestrator.py expects all validators to return Result objects. An unhandled exception will propagate uncaught, potentially crashing the scanning batch or leaving the caller without proper status information. Always wrap error conditions in Result.error().
Can I use async/await in a username scanning module?
Yes. The repository includes async patterns as demonstrated in user_scanner/email_scan/social/mastodon.py. For async validators, use async def and ensure the orchestrator calling the function handles await properly. The same Result return type contract applies.
How do I choose between generic_validate and impersonate_validate?
Use generic_validate for standard sites without aggressive bot protection. Use impersonate_validate when the target site employs Cloudflare, DataDome, or similar anti-bot measures that reject programmatic HTTP clients. The impersonate helper uses browser fingerprinting to bypass these protections.
What metadata should I include in Result.taken()?
Include any publicly visible profile information that enriches the output: fullname, location, bio, joined_date in extra, and avatar/profile image URLs in media. The exact fields depend on what the target platform exposes in its HTML or API response.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →