Instruction: Create an AUTARCH module in the 'osint' category that adult site username osint scanner. The module should follow AUTARCH conventions with DESCRIPTION, AUTHOR, VERSION, CATEGORY attributes and a run() entry point function.

Input:
Module name: adultscan
Category: osint
Description: Adult site username OSINT scanner

Output:
"""
AUTARCH Adult Site Scanner Module
Username OSINT for adult-oriented platforms

Searches usernames across adult content sites, fanfiction platforms,
and related communities.
"""

import sys
import subprocess
import re
import json
from pathlib import Path
from urllib.parse import quote
from concurrent.futures import ThreadPoolExecutor, as_completed

# Module metadata
DESCRIPTION = "Adult site username OSINT scanner"
AUTHOR = "darkHal"
VERSION = "1.3"
CATEGORY = "osint"

sys.path.insert(0, str(Path(__file__).parent.parent))
from core.banner import Colors, clear_screen, display_banner
from core.config import get_config

# Custom sites storage file
from core.paths import get_app_dir as _app_dir
CUSTOM_SITES_FILE = _app_dir() / "custom_adultsites.json"
# Bulk import file
BULK_IMPORT_FILE = _app_dir() / "custom_sites.inf"

# Common username URL patterns for auto-detection
COMMON_PATTERNS = [
    '/user/{}',
    '/users/{}',
    '/u/{}',
    '/profile/{}',
    '/profiles/{}',
    '/member/{}',
    '/members/{}',
    '/@{}',
    '/{}',
    '/people/{}',
    '/account/{}',
    '/id/{}',
    '/{}/profile',
    '/user/{}/profile',
    '/channel/{}',
    '/c/{}',
    '/p/{}',
]

class AdultScanner:
    """Username scanner for adult-oriented sites."""

    # Default site definitions: (name, url_template, method)
    # method: 'status' = check HTTP status, 'content' = check page content
    DEFAULT_SITES = {
        # Fanfiction & Story Sites
        'fanfiction': [
            ('Archive of Our Own', 'https://archiveofourown.org/users/{}/profile', 'status'),
            ('FanFiction.net', 'https://www.fanfiction.net/u/0/{}', 'content'),
            ('FimFiction', 'https://www.fimfiction.net/user/{}', 'status'),
            ('Wattpad', 'https://www.wattpad.com/user/{}', 'status'),
            ('Literotica', 'https://www.literotica.com/stories/memberpage.php?uid=0&username={}', 'content'),
            ('Adult-FanFiction', 'http://members.adult-fanfiction.org/profile.php?no=0&uname={}', 'content'),
            ('Hentai Foundry', 'https://www.hentai-foundry.com/user/{}/profile', 'status'),
            ('SoFurry', 'https://www.sofurry.com/browse/user/{}', 'status'),
            ('Inkbunny', 'https://inkbunny.net/{}', 'status'),
        ],

        # Art & Creative
        'art': [
            ('DeviantArt', 'https://www.deviantart.com/{}', 'status'),
            ('Fur Affinity', 'https://www.furaffinity.net/user/{}/', 'status'),
            ('Newgrounds', 'https://{}.newgrounds.com', 'status'),
            ('Pixiv', 'https://www.pixiv.net/en/users/{}', 'content'),
            ('Rule34', 'https://rule34.xxx/index.php?page=account&s=profile&uname={}', 'content'),
            ('e621', 'https://e621.net/users?name={}', 'content'),
            ('Derpibooru', 'https://derpibooru.org/profiles/{}', 'status'),
            ('Twitter/X', 'https://twitter.com/{}', 'status'),
            ('Tumblr', 'https://{}.tumblr.com', 'status'),
            ('Pillowfort', 'https://www.pillowfort.social/{}', 'status'),
        ],

        # Video & Streaming
        'video': [
            ('Pornhub', 'https://www.pornhub.com/users/{}', 'status'),
            ('XVideos', 'https://www.xvideos.com/profiles/{}', 'status'),
            ('xHamster', 'https://xhamster.com/users/{}', 'status'),
            ('Chaturbate', 'https://chaturbate.com/{}/', 'status'),
            ('OnlyFans', 'https://onlyfans.com/{}', 'status'),
            ('Fansly', 'https://fansly.com/{}', 'status'),
            ('ManyVids', 'https://www.manyvids.com/Profile/0/{}/', 'content'),
            ('PocketStars', 'https://pocketstars.com/{}', 'status'),
        ],

        # Forums & Communities
        'forums': [
            ('Reddit', 'https://www.reddit.com/user/{}', 'status'),
            ('F-List', 'https://www.f-list.net/c/{}', 'status'),
            ('FetLife', 'https://fetlife.com/users/{}', 'content'),
            ('Kink.com', 'https://www.kink.com/model/{}', 'content'),
            ('BDSMLR', 'https://{}.bdsmlr.com', 'status'),
            ('CollarSpace', 'https://www.collarspace.com/view/{}', 'content'),
        ],

        # Dating & Social
        'dating': [
            ('AdultFriendFinder', 'https://adultfriendfinder.com/p/{}', 'content'),
            ('Ashley Madison', 'https://www.ashleymadison.com/{}', 'content'),
            ('Grindr', 'https://www.grindr.com/{}', 'content'),
            ('Scruff', 'https://www.scruff.com/{}', 'content'),
            ('Recon', 'https://www.recon.com/{}', 'content'),
        ],

        # Gaming Related (with adult content)
        'gaming': [
            ('F95zone', 'https://f95zone.to/members/?username={}', 'content'),
            ('LoversLab', 'https://www.loverslab.com/profile/?name={}', 'content'),
            ('ULMF', 'https://ulmf.org/member.php?username={}', 'content'),
            ('Nutaku', 'https://www.nutaku.net/user/{}/', 'content'),
        ],
    }

    def __init__(self):
        self.results = []
        self.config = get_config()
        osint_settings = self.config.get_osint_settings()
        self.timeout = osint_settings['timeout']
        self.max_threads = osint_settings['max_threads']
        # Copy default sites and add custom sites
        self.sites = {k: list(v) for k, v in self.DEFAULT_SITES.items()}
        self.sites['custom'] = []
        self.load_custom_sites()

    def load_custom_sites(self):
        """Load custom sites from JSON file."""
        if CUSTOM_SITES_FILE.exists():
            try:
                with open(CUSTOM_SITES_FILE, 'r') as f:
                    data = json.load(f)
                    self.sites['custom'] = [tuple(site) for site in data.get('sites', [])]
            except Exception as e:
                self.sites['custom'] = []

    def save_custom_sites(self):
        """Save custom sites to JSON file."""
        try:
            data = {'sites': [list(site) for site in self.sites['custom']]}
            with open(CUSTOM_SITES_FILE, 'w') as f:
                json.dump(data, f, indent=2)
            return True
        except Exception as e:
            return False

    def add_custom_site(self):
        """Interactively add a custom site."""
        print(f"\n{Colors.BOLD}Add Custom Site{Colors.RESET}")
        print(f"{Colors.DIM}{'─' * 50}{Colors.RESET}")
        print()
        print(f"{Colors.CYAN}URL Pattern Format:{Colors.RESET}")
        print(f"  Use {Colors.YELLOW}*{Colors.RESET} where the username should go")
        print(f"  Example: {Colors.DIM}https://example.com/user/*{Colors.RESET}")
        print(f"  Example: {Colors.DIM}https://example.com/profile?name=*{Colors.RESET}")
        print()

        # Get site name
        name = input(f"{Colors.WHITE}Site name: {Colors.RESET}").strip()
        if not name:
            self.print_status("Cancelled - no name provided", "warning")
            return

        # Get URL pattern
        url_pattern = input(f"{Colors.WHITE}URL pattern (use * for username): {Colors.RESET}").strip()
        if not url_pattern:
            self.print_status("Cancelled - no URL provided", "warning")
            return

        if '*' not in url_pattern:
            self.print_status("URL must contain * for username placeholder", "error")
            return

        # Convert * to {} for internal format
        url_template = url_pattern.replace('*', '{}')

        # Ensure URL has protocol
        if not url_template.startswith('http://') and not url_template.startswith('https://'):
            url_template = 'https://' + url_template

        # Get detection method
        print()
        print(f"{Colors.CYAN}Detection Method:{Colors.RESET}")
        print(f"  {Colors.GREEN}[1]{Colors.RESET} Status code (default) - Check HTTP response code")
        print(f"  {Colors.GREEN}[2]{Colors.RESET} Content - For sites with custom 404 pages")
        method_choice = input(f"{Colors.WHITE}Select [1]: {Colors.RESET}").strip() or "1"
        method = 'content' if method_choice == '2' else 'status'

        # Add to custom sites
        new_site = (name, url_template, method)
        self.sites['custom'].append(new_site)

        # Save to file
        if self.save_custom_sites():
            self.print_status(f"Added '{name}' to custom sites", "success")
            print(f"{Colors.DIM}  URL: {url_template.replace('{}', '<username>')}{Colors.RESET}")
        else:
            self.print_status("Failed to save custom sites", "error")

    def manage_custom_sites(self):
        """View and manage custom sites."""
        while True:
            clear_screen()
            display_banner()

            print(f"{Colors.BOLD}Manage Custom Sites{Colors.RESET}")
            print(f"{Colors.DIM}{'─' * 50}{Colors.RESET}")
            print()

            custom = self.sites.get('custom', [])
            if not custom:
                print(f"{Colors.YELLOW}No custom sites added yet.{Colors.RESET}")
                print()
                print(f"  {Colors.GREEN}[1]{Colors.RESET} Add New Site")
                print(f"  {Colors.DIM}[0]{Colors.RESET} Back")
                print()

                choice = input(f"{Colors.WHITE}Select: {Colors.RESET}").strip()
                if choice == "1":
                    self.add_custom_site()
                else:
                    break
            else:
                print(f"{Colors.CYAN}Custom Sites ({len(custom)}):{Colors.RESET}")
                print()
                for i, (name, url, method) in enumerate(custom, 1):
                    display_url = url.replace('{}', '*')
                    method_tag = f"[{method}]"
                    print(f"  {Colors.GREEN}[{i}]{Colors.RESET} {name:25} {Colors.DIM}{method_tag}{Colors.RESET}")
                    print(f"      {Colors.DIM}{display_url}{Colors.RESET}")
                print()
                print(f"  {Colors.GREEN}[A]{Colors.RESET} Add New Site")
                print(f"  {Colors.RED}[R]{Colors.RESET} Remove Site")
                print(f"  {Colors.DIM}[0]{Colors.RESET} Back")
                print()

                choice = input(f"{Colors.WHITE}Select: {Colors.RESET}").strip().upper()

                if choice == "0":
                    break
                elif choice == "A":
                    self.add_custom_site()
                elif choice == "R":
                    self.remove_custom_site()

    def remove_custom_site(self):
        """Remove a custom site."""
        custom = self.sites.get('custom', [])
        if not custom:
            self.print_status("No custom sites to remove", "warning")
            return

        print()
        idx_input = input(f"{Colors.WHITE}Enter site number to remove: {Colors.RESET}").strip()

        try:
            idx = int(idx_input) - 1
            if 0 <= idx < len(custom):
                removed = custom.pop(idx)
                if self.save_custom_sites():
                    self.print_status(f"Removed '{removed[0]}'", "success")
                else:
                    self.print_status("Failed to save changes", "error")
            else:
                self.print_status("Invalid selection", "error")
        except ValueError:
            self.print_status("Invalid number", "error")

        input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")

    def auto_detect_site(self):
        """Auto-detect URL pattern for a domain."""
        print(f"\n{Colors.BOLD}Auto-Detect Site Pattern{Colors.RESET}")
        print(f"{Colors.DIM}{'─' * 50}{Colors.RESET}")
        print()
        print(f"{Colors.CYAN}Enter just the domain name and we'll find the pattern.{Colors.RESET}")
        print(f"{Colors.DIM}Example: example.com or www.example.com{Colors.RESET}")
        print()

        # Get domain
        domain = input(f"{Colors.WHITE}Domain: {Colors.RESET}").strip()
        if not domain:
            self.print_status("Cancelled - no domain provided", "warning")
            return

        # Clean up domain
        domain = domain.replace('https://', '').replace('http://', '').rstrip('/')

        # Get test username
        print()
        print(f"{Colors.CYAN}We need a known username to test patterns.{Colors.RESET}")
        print(f"{Colors.DIM}Enter a username that you know EXISTS on this site.{Colors.RESET}")
        test_user = input(f"{Colors.WHITE}Test username: {Colors.RESET}").strip()
        if not test_user:
            self.print_status("Cancelled - no test username provided", "warning")
            return

        print(f"\n{Colors.CYAN}Testing {len(COMMON_PATTERNS)} common URL patterns...{Colors.RESET}\n")

        # Test each pattern
        working_patterns = []
        for i, pattern in enumerate(COMMON_PATTERNS):
            url = f"https://{domain}{pattern}".format(test_user)
            print(f"\r{Colors.DIM}  Testing pattern {i+1}/{len(COMMON_PATTERNS)}: {pattern}{' ' * 20}{Colors.RESET}", end="")

            cmd = f"curl -sI -o /dev/null -w '%{{http_code}}' -L --max-time 5 '{url}' 2>/dev/null"
            success, output, _ = self.run_cmd(cmd, 7)

            if success:
                status_code = output.strip()
                if status_code in ['200', '301', '302']:
                    working_patterns.append((pattern, status_code, url))

        print(f"\r{' ' * 80}\r", end="")  # Clear line

        if not working_patterns:
            print(f"{Colors.YELLOW}No working patterns found.{Colors.RESET}")
            print(f"{Colors.DIM}The site may use a non-standard URL format.{Colors.RESET}")
            print(f"{Colors.DIM}Try using manual add [A] with the correct URL pattern.{Colors.RESET}")
            return

        # Display working patterns
        print(f"{Colors.GREEN}Found {len(working_patterns)} working pattern(s):{Colors.RESET}\n")
        for i, (pattern, status, url) in enumerate(working_patterns, 1):
            status_info = "OK" if status == '200' else f"redirect ({status})"
            print(f"  {Colors.GREEN}[{i}]{Colors.RESET} {pattern:20} {Colors.DIM}({status_info}){Colors.RESET}")
            print(f"      {Colors.DIM}{url}{Colors.RESET}")
        print()

        # Let user select
        print(f"  {Colors.DIM}[0]{Colors.RESET} Cancel")
        print()

        choice = input(f"{Colors.WHITE}Select pattern to add: {Colors.RESET}").strip()

        try:
            idx = int(choice) - 1
            if 0 <= idx < len(working_patterns):
                selected_pattern, status, _ = working_patterns[idx]
                url_template = f"https://{domain}{selected_pattern}"

                # Get site name
                default_name = domain.split('.')[0].title()
                name = input(f"{Colors.WHITE}Site name [{default_name}]: {Colors.RESET}").strip() or default_name

                # Determine method based on status
                method = 'status' if status == '200' else 'content'

                # Add to custom sites
                new_site = (name, url_template, method)
                self.sites['custom'].append(new_site)

                if self.save_custom_sites():
                    self.print_status(f"Added '{name}' to custom sites", "success")
                    print(f"{Colors.DIM}  Pattern: {url_template.replace('{}', '*')}{Colors.RESET}")
                else:
                    self.print_status("Failed to save custom sites", "error")
            elif choice != "0":
                self.print_status("Cancelled", "warning")
        except ValueError:
            if choice != "0":
                self.print_status("Invalid selection", "error")

    def probe_domain(self, domain: str, test_user: str, quiet: bool = False) -> list:
        """Probe a domain for working URL patterns. Returns list of (pattern, status_code, url)."""
        domain = domain.replace('https://', '').replace('http://', '').rstrip('/')
        working_patterns = []

        for i, pattern in enumerate(COMMON_PATTERNS):
            url = f"https://{domain}{pattern}".format(test_user)
            if not quiet:
                print(f"\r{Colors.DIM}  Testing {domain}: pattern {i+1}/{len(COMMON_PATTERNS)}{' ' * 20}{Colors.RESET}", end="")

            cmd = f"curl -sI -o /dev/null -w '%{{http_code}}' -L --max-time 5 '{url}' 2>/dev/null"
            success, output, _ = self.run_cmd(cmd, 7)

            if success:
                status_code = output.strip()
                if status_code in ['200', '301', '302']:
                    working_patterns.append((pattern, status_code, url))
                    # For bulk mode, take first working pattern and stop
                    if quiet:
                        break

        if not quiet:
            print(f"\r{' ' * 80}\r", end="")

        return working_patterns

    def bulk_import(self):
        """Bulk import sites from custom_sites.inf file."""
        print(f"\n{Colors.BOLD}Bulk Import Sites{Colors.RESET}")
        print(f"{Colors.DIM}{'─' * 50}{Colors.RESET}")
        print()

        # Check if file exists, create template if not
        if not BULK_IMPORT_FILE.exists():
            print(f"{Colors.YELLOW}Bulk import file not found.{Colors.RESET}")
            print(f"{Colors.DIM}Creating template at: {BULK_IMPORT_FILE}{Colors.RESET}")
            print()

            create = input(f"{Colors.WHITE}Create template file? (y/n): {Colors.RESET}").strip().lower()
            if create == 'y':
                template = """# AUTARCH Adult Site Scanner - Bulk Import File
# Add one domain per line (without http:// or https://)
# Lines starting with # are comments
#
# Example:
# example.com
# another-site.net
# subdomain.site.org
#
# After adding domains, run Bulk Import [B] again
# and provide a test username that exists on these sites.

"""
                with open(BULK_IMPORT_FILE, 'w') as f:
                    f.write(template)
                self.print_status(f"Created {BULK_IMPORT_FILE}", "success")
                print(f"{Colors.DIM}Edit this file and add domains, then run Bulk Import again.{Colors.RESET}")
            return

        # Read domains from file
        domains = []
        with open(BULK_IMPORT_FILE, 'r') as f:
            for line in f:
                line = line.strip()
                # Skip empty lines and comments
                if line and not line.startswith('#'):
                    # Clean up domain
                    domain = line.replace('https://', '').replace('http://', '').rstrip('/')
                    if domain:
                        domains.append(domain)

        if not domains:
            print(f"{Colors.YELLOW}No domains found in {BULK_IMPORT_FILE.name}{Colors.RESET}")
            print(f"{Colors.DIM}Add domains (one per line) and try again.{Colors.RESET}")
            return

        print(f"{Colors.CYAN}Found {len(domains)} domain(s) in {BULK_IMPORT_FILE.name}:{Colors.RESET}")
        for d in domains[:10]:
            print(f"  {Colors.DIM}-{Colors.RESET} {d}")
        if len(domains) > 10:
            print(f"  {Colors.DIM}... and {len(domains) - 10} more{Colors.RESET}")
        print()

        # Check which domains are already added
        existing_domains = set()
        for name, url, method in self.sites.get('custom', []):
            # Extract domain from URL template
            try:
                from urllib.parse import urlparse
                parsed = urlparse(url.replace('{}', 'test'))
                existing_domains.add(parsed.netloc.lower())
            except:
                pass

        new_domains = [d for d in domains if d.lower() not in existing_domains]
        skipped = len(domains) - len(new_domains)

        if skipped > 0:
            print(f"{Colors.YELLOW}Skipping {skipped} already-added domain(s){Colors.RESET}")

        if not new_domains:
            print(f"{Colors.GREEN}All domains already added!{Colors.RESET}")
            return

        print(f"{Colors.CYAN}Will scan {len(new_domains)} new domain(s){Colors.RESET}")
        print()

        # Get test username
        print(f"{Colors.CYAN}We need a test username to probe URL patterns.{Colors.RESET}")
        print(f"{Colors.DIM}Use a common username that likely exists on most sites.{Colors.RESET}")
        print(f"{Colors.DIM}Example: admin, test, user, john, etc.{Colors.RESET}")
        print()
        test_user = input(f"{Colors.WHITE}Test username: {Colors.RESET}").strip()
        if not test_user:
            self.print_status("Cancelled - no test username provided", "warning")
            return

        print(f"\n{Colors.CYAN}Scanning {len(new_domains)} domains...{Colors.RESET}\n")

        # Scan each domain
        added = 0
        failed = []

        for i, domain in enumerate(new_domains):
            print(f"{Colors.DIM}[{i+1}/{len(new_domains)}] Scanning {domain}...{Colors.RESET}")

            # Use quiet mode to get first working pattern
            patterns = self.probe_domain(domain, test_user, quiet=True)

            if patterns:
                pattern, status, url = patterns[0]
                url_template = f"https://{domain}{pattern}"
                name = domain.split('.')[0].title()
                method = 'status' if status == '200' else 'content'

                # Add to custom sites
                new_site = (name, url_template, method)
                self.sites['custom'].append(new_site)
                added += 1
                print(f"  {Colors.GREEN}[+]{Colors.RESET} Added {name}: {pattern}")
            else:
                failed.append(domain)
                print(f"  {Colors.RED}[X]{Colors.RESET} No pattern found")

        # Save results
        if added > 0:
            if self.save_custom_sites():
                print(f"\n{Colors.GREEN}Successfully added {added} site(s){Colors.RESET}")
            else:
                print(f"\n{Colors.RED}Failed to save custom sites{Colors.RESET}")

        if failed:
            print(f"\n{Colors.YELLOW}Failed to detect patterns for {len(failed)} domain(s):{Colors.RESET}")
            for d in failed[:5]:
                print(f"  {Colors.DIM}-{Colors.RESET} {d}")
            if len(failed) > 5:
                print(f"  {Colors.DIM}... and {len(failed) - 5} more{Colors.RESET}")
            print(f"{Colors.DIM}Try adding these manually with [A] or [D]{Colors.RESET}")

        # Offer to clear the import file
        print()
        clear_file = input(f"{Colors.WHITE}Clear import file? (y/n): {Colors.RESET}").strip().lower()
        if clear_file == 'y':
            # Keep the header comments
            header = """# AUTARCH Adult Site Scanner - Bulk Import File
# Add one domain per line (without http:// or https://)
# Lines starting with # are comments

"""
            with open(BULK_IMPORT_FILE, 'w') as f:
                f.write(header)
            self.print_status("Import file cleared", "success")

    def print_status(self, message: str, status: str = "info"):
        colors = {"info": Colors.CYAN, "success": Colors.GREEN, "warning": Colors.YELLOW, "error": Colors.RED}
        symbols = {"info": "*", "success": "+", "warning": "!", "error": "X"}
        print(f"{colors.get(status, Colors.WHITE)}[{symbols.get(status, '*')}] {message}{Colors.RESET}")

    def run_cmd(self, cmd: str, timeout: int = 10) -> tuple:
        try:
            result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
            return result.returncode == 0, result.stdout.strip(), result.stderr.strip()
        except subprocess.TimeoutExpired:
            return False, "", "timeout"
        except Exception as e:
            return False, "", str(e)

    def check_site(self, site_info: tuple, username: str) -> dict:
        """Check if username exists on a site."""
        name, url_template, method = site_info

        # Handle special URL formats
        if '{}' in url_template:
            url = url_template.format(username)
        else:
            url = url_template + username

        result = {
            'site': name,
            'url': url,
            'found': False,
            'status': 'unknown'
        }

        # Use curl to check
        cmd = f"curl -sI -o /dev/null -w '%{{http_code}}' -L --max-time {self.timeout} '{url}' 2>/dev/null"
        success, output, _ = self.run_cmd(cmd, self.timeout + 2)

        if success:
            status_code = output.strip()
            if method == 'status':
                # Check HTTP status code
                if status_code == '200':
                    result['found'] = True
                    result['status'] = 'found'
                elif status_code in ['301', '302']:
                    result['found'] = True
                    result['status'] = 'redirect'
                elif status_code == '404':
                    result['status'] = 'not_found'
                else:
                    result['status'] = f'http_{status_code}'
            else:
                # For content-based checks, we need to fetch the page
                if status_code == '200':
                    # Could do content analysis here
                    result['found'] = True
                    result['status'] = 'possible'
                elif status_code == '404':
                    result['status'] = 'not_found'
                else:
                    result['status'] = f'http_{status_code}'
        else:
            result['status'] = 'error'

        return result

    def scan_username(self, username: str, categories: list = None):
        """Scan username across selected site categories."""
        if categories is None:
            categories = list(self.sites.keys())

        # Collect all sites to scan
        sites_to_scan = []
        for cat in categories:
            if cat in self.sites:
                sites_to_scan.extend(self.sites[cat])

        print(f"\n{Colors.CYAN}Scanning {len(sites_to_scan)} sites for username: {username}{Colors.RESET}")
        print(f"{Colors.DIM}This may take a few minutes...{Colors.RESET}\n")

        self.results = []
        found_count = 0

        # Use thread pool for parallel scanning
        with ThreadPoolExecutor(max_workers=self.max_threads) as executor:
            futures = {executor.submit(self.check_site, site, username): site for site in sites_to_scan}

            for i, future in enumerate(as_completed(futures)):
                result = future.result()
                self.results.append(result)

                # Display progress
                if result['found']:
                    found_count += 1
                    status_color = Colors.GREEN if result['status'] == 'found' else Colors.YELLOW
                    print(f"  {status_color}[+]{Colors.RESET} {result['site']:25} {result['url']}")
                else:
                    # Show progress indicator
                    print(f"\r{Colors.DIM}  Checked {i+1}/{len(sites_to_scan)} sites, found {found_count}...{Colors.RESET}", end="")

        print(f"\r{' ' * 60}\r", end="")  # Clear progress line
        return self.results

    def display_results(self):
        """Display scan results."""
        found = [r for r in self.results if r['found']]
        not_found = [r for r in self.results if not r['found']]

        print(f"\n{Colors.BOLD}{'─' * 60}{Colors.RESET}")
        print(f"{Colors.BOLD}Scan Results{Colors.RESET}")
        print(f"{Colors.BOLD}{'─' * 60}{Colors.RESET}\n")

        if found:
            print(f"{Colors.GREEN}Found ({len(found)} sites):{Colors.RESET}\n")
            for r in found:
                status_note = f" ({r['status']})" if r['status'] not in ['found'] else ""
                print(f"  {Colors.GREEN}+{Colors.RESET} {r['site']:25} {r['url']}{Colors.DIM}{status_note}{Colors.RESET}")
        else:
            print(f"{Colors.YELLOW}No profiles found.{Colors.RESET}")

        print(f"\n{Colors.DIM}Total sites checked: {len(self.results)}{Colors.RESET}")
        print(f"{Colors.DIM}Profiles found: {len(found)}{Colors.RESET}")

    def export_results(self, filename: str):
        """Export results to file."""
        found = [r for r in self.results if r['found']]

        with open(filename, 'w') as f:
            f.write(f"Username OSINT Results\n")
            f.write(f"{'=' * 50}\n\n")
            f.write(f"Found Profiles ({len(found)}):\n\n")
            for r in found:
                f.write(f"{r['site']}: {r['url']}\n")

        self.print_status(f"Results exported to {filename}", "success")

    def show_menu(self):
        """Display main menu."""
        clear_screen()
        display_banner()

        print(f"{Colors.GREEN}{Colors.BOLD}  Adult Site Scanner{Colors.RESET}")
        print(f"{Colors.DIM}  Username OSINT for adult platforms{Colors.RESET}")
        print(f"{Colors.DIM}  {'─' * 50}{Colors.RESET}")
        print()

        # Show category counts
        total = sum(len(sites) for sites in self.sites.values())
        custom_count = len(self.sites.get('custom', []))
        print(f"{Colors.DIM}  Sites in database: {total} ({custom_count} custom){Colors.RESET}")
        print()

        print(f"  {Colors.CYAN}Scan Categories:{Colors.RESET}")
        print(f"  {Colors.GREEN}[1]{Colors.RESET} Full Scan (all categories)")
        print(f"  {Colors.GREEN}[2]{Colors.RESET} Fanfiction & Story Sites")
        print(f"  {Colors.GREEN}[3]{Colors.RESET} Art & Creative Sites")
        print(f"  {Colors.GREEN}[4]{Colors.RESET} Video & Streaming Sites")
        print(f"  {Colors.GREEN}[5]{Colors.RESET} Forums & Communities")
        print(f"  {Colors.GREEN}[6]{Colors.RESET} Dating & Social Sites")
        print(f"  {Colors.GREEN}[7]{Colors.RESET} Gaming Related Sites")
        print(f"  {Colors.GREEN}[8]{Colors.RESET} Custom Sites Only")
        print(f"  {Colors.GREEN}[9]{Colors.RESET} Custom Category Selection")
        print()
        print(f"  {Colors.CYAN}Site Management:{Colors.RESET}")
        print(f"  {Colors.GREEN}[A]{Colors.RESET} Add Custom Site (manual)")
        print(f"  {Colors.GREEN}[D]{Colors.RESET} Auto-Detect Site Pattern")
        print(f"  {Colors.GREEN}[B]{Colors.RESET} Bulk Import from File")
        print(f"  {Colors.GREEN}[M]{Colors.RESET} Manage Custom Sites")
        print(f"  {Colors.GREEN}[L]{Colors.RESET} List All Sites")
        print()
        print(f"  {Colors.DIM}[0]{Colors.RESET} Back")
        print()

    def select_categories(self) -> list:
        """Let user select multiple categories."""
        print(f"\n{Colors.BOLD}Select Categories (comma-separated):{Colors.RESET}")
        print()

        cat_list = list(self.sites.keys())
        for i, cat in enumerate(cat_list, 1):
            count = len(self.sites[cat])
            print(f"  [{i}] {cat.title():20} ({count} sites)")

        print()
        selection = input(f"{Colors.WHITE}Enter numbers (e.g., 1,2,3): {Colors.RESET}").strip()

        selected = []
        try:
            for num in selection.split(','):
                idx = int(num.strip()) - 1
                if 0 <= idx < len(cat_list):
                    selected.append(cat_list[idx])
        except:
            pass

        return selected if selected else None

    def list_sites(self):
        """List all sites in database."""
        clear_screen()
        display_banner()

        print(f"{Colors.BOLD}Site Database{Colors.RESET}")
        print(f"{Colors.DIM}{'─' * 60}{Colors.RESET}\n")

        for category, sites in self.sites.items():
            if not sites:
                continue
            color = Colors.YELLOW if category == 'custom' else Colors.GREEN
            print(f"{color}{category.upper()} ({len(sites)} sites){Colors.RESET}")
            for name, url, method in sites:
                if category == 'custom':
                    display_url = url.replace('{}', '*')
                    print(f"  {Colors.DIM}-{Colors.RESET} {name} {Colors.DIM}({display_url}){Colors.RESET}")
                else:
                    print(f"  {Colors.DIM}-{Colors.RESET} {name}")
            print()

        input(f"{Colors.WHITE}Press Enter to continue...{Colors.RESET}")

    def run_scan(self, categories: list = None):
        """Run a scan with given categories."""
        username = input(f"\n{Colors.WHITE}Enter username to search: {Colors.RESET}").strip()
        if not username:
            return

        self.scan_username(username, categories)
        self.display_results()

        # Export option
        export = input(f"\n{Colors.WHITE}Export results to file? (y/n): {Colors.RESET}").strip().lower()
        if export == 'y':
            filename = f"{username}_adultscan.txt"
            self.export_results(filename)

    def run(self):
        """Main loop."""
        while True:
            self.show_menu()
            try:
                choice = input(f"{Colors.WHITE}  Select: {Colors.RESET}").strip().upper()

                if choice == "0":
                    break
                elif choice == "1":
                    self.run_scan()  # All categories
                elif choice == "2":
                    self.run_scan(['fanfiction'])
                elif choice == "3":
                    self.run_scan(['art'])
                elif choice == "4":
                    self.run_scan(['video'])
                elif choice == "5":
                    self.run_scan(['forums'])
                elif choice == "6":
                    self.run_scan(['dating'])
                elif choice == "7":
                    self.run_scan(['gaming'])
                elif choice == "8":
                    if self.sites.get('custom'):
                        self.run_scan(['custom'])
                    else:
                        self.print_status("No custom sites added yet. Use [A] to add sites.", "warning")
                        input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
                        continue
                elif choice == "9":
                    cats = self.select_categories()
                    if cats:
                        self.run_scan(cats)
                elif choice == "A":
                    self.add_custom_site()
                    input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
                    continue
                elif choice == "D":
                    self.auto_detect_site()
                    input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
                    continue
                elif choice == "B":
                    self.bulk_import()
                    input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
                    continue
                elif choice == "M":
                    self.manage_custom_sites()
                    continue
                elif choice == "L":
                    self.list_sites()
                    continue

                if choice in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]:
                    input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")

            except (EOFError, KeyboardInterrupt):
                break

def run():
    AdultScanner().run()

if __name__ == "__main__":
    run()


Instruction: Create an AUTARCH module in the 'core' category that autonomous agent for task execution. The module should follow AUTARCH conventions with DESCRIPTION, AUTHOR, VERSION, CATEGORY attributes and a run() entry point function.

Input:
Module name: agent
Category: core
Description: Autonomous agent for task execution

Output:
"""
AUTARCH Agent Module
Interactive interface for running autonomous agent tasks

This module provides an interface to give tasks to the autonomous agent
and watch it work through them step by step.
"""

import sys
from pathlib import Path

# Module metadata
DESCRIPTION = "Autonomous agent for task execution"
AUTHOR = "darkHal"
VERSION = "1.0"
CATEGORY = "core"

# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent))

from core.agent import Agent, AgentState, AgentStep, AgentResult
from core.tools import get_tool_registry
from core.llm import get_llm, LLMError
from core.banner import Colors, clear_screen, display_banner

class AgentInterface:
    """Interactive interface for the AUTARCH agent."""

    def __init__(self):
        self.agent = None
        self.llm = get_llm()
        self.tools = get_tool_registry()

    def print_status(self, message: str, status: str = "info"):
        """Print a status message."""
        colors = {"info": Colors.CYAN, "success": Colors.GREEN, "warning": Colors.YELLOW, "error": Colors.RED}
        symbols = {"info": "*", "success": "+", "warning": "!", "error": "X"}
        print(f"{colors.get(status, Colors.WHITE)}[{symbols.get(status, '*')}] {message}{Colors.RESET}")

    def print_header(self, text: str):
        """Print a section header."""
        print(f"\n{Colors.BOLD}{Colors.WHITE}{text}{Colors.RESET}")
        print(f"{Colors.DIM}{'─' * 50}{Colors.RESET}")

    def show_tools(self):
        """Display available tools."""
        self.print_header("Available Tools")

        tools = self.tools.list_tools()
        for tool in tools:
            print(f"\n  {Colors.CYAN}{tool.name}{Colors.RESET} [{tool.category}]")
            print(f"  {Colors.DIM}{tool.description}{Colors.RESET}")
            if tool.parameters:
                for param in tool.parameters:
                    req = "*" if param.required else ""
                    print(f"    - {param.name}{req}: {param.description}")

    def on_step_callback(self, step: AgentStep):
        """Callback for when agent completes a step."""
        print(f"\n{Colors.DIM}{'─' * 40}{Colors.RESET}")

    def on_state_callback(self, state: AgentState):
        """Callback for agent state changes."""
        state_colors = {
            AgentState.IDLE: Colors.WHITE,
            AgentState.THINKING: Colors.MAGENTA,
            AgentState.EXECUTING: Colors.BLUE,
            AgentState.WAITING_USER: Colors.YELLOW,
            AgentState.COMPLETE: Colors.GREEN,
            AgentState.ERROR: Colors.RED,
        }
        color = state_colors.get(state, Colors.WHITE)
        # Only show state for key transitions
        if state in [AgentState.COMPLETE, AgentState.ERROR]:
            print(f"{color}[State: {state.value}]{Colors.RESET}")

    def run_task(self, task: str) -> AgentResult:
        """Run a task through the agent.

        Args:
            task: Task description.

        Returns:
            AgentResult with execution details.
        """
        self.agent = Agent(
            llm=self.llm,
            tools=self.tools,
            max_steps=20,
            verbose=True
        )

        self.agent.on_step = self.on_step_callback
        self.agent.on_state_change = self.on_state_callback

        return self.agent.run(task)

    def interactive_loop(self):
        """Run interactive task input loop."""
        self.print_header("Agent Interface")
        print(f"\n{Colors.WHITE}Enter a task for the agent to complete.")
        print(f"Type 'tools' to see available tools.")
        print(f"Type 'exit' to return to main menu.{Colors.RESET}\n")

        while True:
            try:
                print(f"{Colors.DIM}{'─' * 50}{Colors.RESET}")
                task = input(f"\n{Colors.GREEN}Task:{Colors.RESET} ").strip()

                if not task:
                    continue

                if task.lower() == 'exit':
                    break

                if task.lower() == 'tools':
                    self.show_tools()
                    continue

                if task.lower() == 'help':
                    print(f"\n{Colors.WHITE}Commands:{Colors.RESET}")
                    print(f"  {Colors.CYAN}tools{Colors.RESET}  - Show available tools")
                    print(f"  {Colors.CYAN}exit{Colors.RESET}   - Return to main menu")
                    print(f"  {Colors.CYAN}help{Colors.RESET}   - Show this help")
                    print(f"\n{Colors.WHITE}Or enter a task description for the agent.{Colors.RESET}")
                    continue

                # Run the task
                print(f"\n{Colors.CYAN}[*] Starting agent...{Colors.RESET}\n")

                result = self.run_task(task)

                # Show result summary
                print(f"\n{Colors.DIM}{'═' * 50}{Colors.RESET}")
                if result.success:
                    print(f"{Colors.GREEN}[+] Task completed successfully{Colors.RESET}")
                    print(f"\n{Colors.WHITE}Summary:{Colors.RESET} {result.summary}")
                else:
                    print(f"{Colors.RED}[X] Task failed{Colors.RESET}")
                    if result.error:
                        print(f"{Colors.RED}Error:{Colors.RESET} {result.error}")
                    if result.summary:
                        print(f"{Colors.WHITE}Summary:{Colors.RESET} {result.summary}")

                print(f"\n{Colors.DIM}Steps taken: {len(result.steps)}{Colors.RESET}")

            except (EOFError, KeyboardInterrupt):
                print(f"\n\n{Colors.YELLOW}[!] Interrupted{Colors.RESET}")
                break

    def run(self):
        """Module entry point."""
        clear_screen()
        display_banner()

        print(f"{Colors.BOLD}{Colors.WHITE}  AUTARCH Autonomous Agent{Colors.RESET}")
        print(f"{Colors.DIM}  {'─' * 50}{Colors.RESET}")

        # Check if model is loaded
        if not self.llm.is_loaded:
            self.print_status("Loading model...", "info")
            try:
                self.llm.load_model(verbose=True)
            except LLMError as e:
                self.print_status(f"Failed to load model: {e}", "error")
                self.print_status("Please run setup to configure a model.", "warning")
                input(f"\n{Colors.WHITE}Press Enter to continue...{Colors.RESET}")
                return

        self.interactive_loop()

def run():
    """Module entry point."""
    interface = AgentInterface()
    interface.run()

if __name__ == "__main__":
    run()