""" Drop Status Checker ==================== Dedicated RDAP checker for dropped domains. Correctly identifies pending_delete, redemption, and available status. """ import httpx import logging from dataclasses import dataclass from typing import Optional logger = logging.getLogger(__name__) # RDAP endpoints for different TLDs RDAP_ENDPOINTS = { # ccTLDs 'ch': 'https://rdap.nic.ch/domain/', 'li': 'https://rdap.nic.ch/domain/', 'de': 'https://rdap.denic.de/domain/', # gTLDs via CentralNic 'online': 'https://rdap.centralnic.com/online/domain/', 'xyz': 'https://rdap.centralnic.com/xyz/domain/', 'club': 'https://rdap.nic.club/domain/', # gTLDs via Afilias/Donuts 'info': 'https://rdap.afilias.net/rdap/info/domain/', 'biz': 'https://rdap.afilias.net/rdap/biz/domain/', 'org': 'https://rdap.publicinterestregistry.org/rdap/org/domain/', # Google TLDs 'dev': 'https://rdap.nic.google/domain/', 'app': 'https://rdap.nic.google/domain/', } @dataclass class DropStatus: """Status of a dropped domain.""" domain: str status: str # 'available', 'dropping_soon', 'taken', 'unknown' rdap_status: list[str] can_register_now: bool should_monitor: bool message: str async def check_drop_status(domain: str) -> DropStatus: """ Check the real status of a dropped domain via RDAP. Returns: DropStatus with one of: - 'available': Domain can be registered NOW - 'dropping_soon': Domain is in pending delete/redemption (monitor it!) - 'taken': Domain was re-registered - 'unknown': Could not determine status """ tld = domain.split('.')[-1].lower() endpoint = RDAP_ENDPOINTS.get(tld) if not endpoint: # Try generic lookup logger.warning(f"No RDAP endpoint for .{tld}, returning unknown") return DropStatus( domain=domain, status='unknown', rdap_status=[], can_register_now=False, should_monitor=False, message=f"No RDAP endpoint for .{tld}" ) url = f"{endpoint}{domain}" try: async with httpx.AsyncClient(timeout=10) as client: resp = await client.get(url) # 404 = Domain not found = AVAILABLE! if resp.status_code == 404: return DropStatus( domain=domain, status='available', rdap_status=[], can_register_now=True, should_monitor=False, message="Domain is available for registration!" ) # 200 = Domain exists in registry if resp.status_code == 200: data = resp.json() rdap_status = data.get('status', []) status_lower = ' '.join(str(s).lower() for s in rdap_status) # Check for pending delete / redemption status is_pending = any(x in status_lower for x in [ 'pending delete', 'pendingdelete', 'pending purge', 'pendingpurge', 'redemption period', 'redemptionperiod', 'pending restore', 'pendingrestore', ]) if is_pending: return DropStatus( domain=domain, status='dropping_soon', rdap_status=rdap_status, can_register_now=False, should_monitor=True, message="Domain is being deleted. Track it to get notified when available!" ) # Domain is actively registered return DropStatus( domain=domain, status='taken', rdap_status=rdap_status, can_register_now=False, should_monitor=False, message="Domain was re-registered" ) # Other status code logger.warning(f"RDAP returned {resp.status_code} for {domain}") return DropStatus( domain=domain, status='unknown', rdap_status=[], can_register_now=False, should_monitor=False, message=f"RDAP returned HTTP {resp.status_code}" ) except httpx.TimeoutException: logger.warning(f"RDAP timeout for {domain}") return DropStatus( domain=domain, status='unknown', rdap_status=[], can_register_now=False, should_monitor=False, message="RDAP timeout" ) except Exception as e: logger.warning(f"RDAP error for {domain}: {e}") return DropStatus( domain=domain, status='unknown', rdap_status=[], can_register_now=False, should_monitor=False, message=str(e) )