import disposable_email_domains from ..models import DisposableResult # Extended free provider list FREE_PROVIDERS = { "gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "aol.com", "icloud.com", "protonmail.com", "zoho.com", "mail.com", "yandex.com", "yandex.ru", "gmx.com", "gmx.net", "live.com", "msn.com", "me.com", "mac.com", "googlemail.com", "yahoo.co.uk", "yahoo.fr", "yahoo.de", "yahoo.it", "yahoo.es", "yahoo.com.br", "hotmail.fr", "hotmail.de", "hotmail.it", "hotmail.es", "hotmail.co.uk", "fastmail.com", "tutanota.com", "pm.me", "proton.me", "hey.com", "duck.com", # ISP mailbox domains: legitimate consumer mailboxes handled by the # deterministic free-provider path, never by the ML advisory (the model has # no training signal for them and flagged sbcglobal.net as disposable). "sbcglobal.net", "att.net", "comcast.net", "verizon.net", "bellsouth.net", "cox.net", "charter.net", "earthlink.net", "roadrunner.com", "rr.com", "shaw.ca", "rogers.com", "btinternet.com", "sky.com", "virginmedia.com", "orange.fr", "wanadoo.fr", "free.fr", "laposte.net", "web.de", "t-online.de", "arcor.de", "libero.it", "virgilio.it", "terra.com.br", "uol.com.br", "bol.com.br", "seznam.cz", "naver.com", "daum.net", "hanmail.net", "rediffmail.com", } # Role-based email prefixes ROLE_BASED_PREFIXES = { "admin", "info", "support", "help", "sales", "contact", "hello", "hi", "noreply", "no-reply", "noreply", "donotreply", "do-not-reply", "webmaster", "postmaster", "abuse", "security", "billing", "accounts", "hr", "humanresources", "marketing", "team", "staff", "office", "enquiries", "enquiry", "inquiry", "feedback", "service", "services", "care", "press", "media", "privacy", "legal", "compliance", "jobs", "careers", "recruitment", "newsletter", "notifications", "alerts", "updates", "root", "sys", "sysadmin", "system", "ops", "devops", "dev", "api", "mail", "email", "bounce", "mailer" } # Nice names for common free providers PROVIDER_MAP = { "gmail.com": "Gmail (Google)", "yahoo.com": "Yahoo Mail", "hotmail.com": "Hotmail (Microsoft)", "outlook.com": "Outlook (Microsoft)", "icloud.com": "iCloud (Apple)", "protonmail.com": "ProtonMail", "proton.me": "Proton Mail", "zoho.com": "Zoho Mail", } def domain_in_blocklist(domain: str) -> bool: """Exact-match against the disposable blocklist (the deterministic truth).""" return domain in disposable_email_domains.blocklist def domain_is_known(domain: str) -> bool: """True when a curated list already answers for this domain. The ML signal runs ONLY on domains outside every curated list - those are the ones a static blocklist cannot classify, and the only place a model adds value instead of risk. """ return domain in disposable_email_domains.blocklist or domain in FREE_PROVIDERS def check_disposable(email: str, domain: str) -> DisposableResult: """ Layer 4: Comprehensive disposable, free provider, and role-based detection. """ local_part = email.split('@')[0].lower() # Disposable domain check (100K+ domains database) is_disposable = domain in disposable_email_domains.blocklist # Free provider check is_free = domain in FREE_PROVIDERS # Role-based check (exact match + prefix match) is_role = ( local_part in ROLE_BASED_PREFIXES or any(local_part.startswith(prefix) for prefix in ROLE_BASED_PREFIXES) ) # Determine provider name provider_name = None if is_free: provider_name = PROVIDER_MAP.get(domain, "Free Email Provider") elif is_disposable: provider_name = "Disposable/Temporary Email" return DisposableResult( is_disposable=is_disposable, is_free_provider=is_free, is_role_based=is_role, provider_name=provider_name )