Expected Function Signature for Email Scanning Modules in user-scanner

Every email-scanning module in user-scanner must expose a single asynchronous validator function named validate_<service> that takes an email string and returns a Result object.

The kaifcodec/user-scanner repository defines a strict, uniform interface for all email validation modules. This design pattern enables the core orchestrator to dynamically discover and invoke any email-checking module without service-specific logic.

Mandatory Function Signature

All email-scanning modules implement the following asynchronous signature:

async def validate_<service>(email: str) -> Result
  • email: str — the email address to validate
  • Result — a return type class defined in user_scanner/core/result.py that encapsulates three possible states: available(), taken(...), or error(...)

The function name follows the pattern validate_<service> where <service> is the lowercase identifier of the target platform.

Real Implementations in the Codebase

The user-scanner source code demonstrates this pattern across multiple service categories:

Service File Path Function
Instagram user_scanner/email_scan/social/instagram.py validate_instagram
WomanLog user_scanner/email_scan/women_health/womanlog.py validate_womanlog
Skyscanner user_scanner/email_scan/travel/skyscanner.py validate_skyscanner

As implemented in kaifcodec/user-scanner, these validators reside under user_scanner/email_scan/<category>/<service>.py and are dynamically imported by the engine at runtime.

The Result Class Contract

The Result class in user_scanner/core/result.py provides three factory methods for consistent status reporting:

  • Result.available() — email not found on the service
  • Result.taken(extra={...}) — email registered; optional extra dict for profile metadata
  • Result.error(message) — validation failed due to network or parsing issues

Minimal Working Example

Create a new email-scanning module by following this template:


# user_scanner/email_scan/productivity/examplemail.py

from user_scanner.core.result import Result


async def validate_examplemail(email: str) -> Result:
    """
    Check if email is registered on ExampleMail service.
    """
    # Perform HTTP request or API lookup

    response = await fetch_email_status(email)
    
    if response.status == 200:
        return Result.taken(extra={"profile_url": response.data["url"]})
    elif response.status == 404:
        return Result.available()
    else:
        return Result.error(f"Unexpected status: {response.status}")

Invoking Validators Through the Engine

The core orchestrator in user_scanner/engine.py loads modules and executes validators without hardcoded service logic:

from user_scanner.engine import Engine

engine = Engine()

# Automatically routes to the correct validate_<service> function

result = await engine.validate_email("user@examplemail.com")

print(result.status)        # "available" | "taken" | "error"

print(result.extra)         # dict with additional metadata (if taken)

Architecture Benefits

The expected function signature for email scanning modules delivers three critical advantages:

  1. Automatic discovery — the engine scans user_scanner/email_scan/ subdirectories and imports any module matching the naming convention
  2. Type safety — consistent Result return type eliminates brittle string parsing across the codebase
  3. Testability — isolated async functions with no external dependencies are straightforward to mock and unit test

Summary

  • All email-scanning modules must define async def validate_<service>(email: str) -> Result
  • Validator functions live in user_scanner/email_scan/<category>/<service>.py
  • The Result class from user_scanner/core/result.py standardizes return values
  • The engine in user_scanner/engine.py dynamically imports and executes validators
  • This contract ensures uniform behavior across Instagram, Skyscanner, WomanLog, and any future services

Frequently Asked Questions

What happens if a module doesn't follow the validate_ naming convention?

The dynamic import mechanism in user_scanner/engine.py will fail to discover the function. Only modules exporting the exact validate_<service> pattern are registered as validators.

Can validate_ functions accept additional parameters beyond email?

No — the core orchestrator invokes all validators with a single positional email argument. Pass configuration through module-level constants or environment variables instead.

Is the Result class a dataclass or a custom object?

According to user_scanner/core/result.py, Result is a custom class with static factory methods. It encapsulates state internally and exposes status, extra, and boolean properties like is_available for caller inspection.

Why must email validators be asynchronous?

Network I/O dominates email validation workflows. The async design in kaifcodec/user-scanner allows the engine to run hundreds of concurrent checks across different services without thread overhead.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →