Nevada's entity search (esos.nv.gov/SilverFlume) is behind Imperva Incapsula bot
protection that blocks headless + the residential proxy IPs, and NV has no public
open-data API/bulk dataset. The old adapter scraped the blocked challenge page and
returned available=False for everything (would tell customers a free name is taken)
and also had a NameError bug (f"${CODE}..."). Now NV detects the Incapsula
challenge and returns available=None ('could not determine') with a note to verify
manually on SilverFlume -- never a false 'taken', so it never wrongly blocks an
order. TX remains fully automated via the open-data API.
102 lines
4 KiB
Python
102 lines
4 KiB
Python
"""Nevada — SilverFlume SOS portal automation.
|
|
|
|
Name search implemented via the public business entity search.
|
|
LLC/Corp filing selectors pending live portal verification.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
from scripts.formation.base import (
|
|
StatePortal,
|
|
NameSearchResult,
|
|
FormationOrder,
|
|
FilingResult,
|
|
FilingStatus,
|
|
)
|
|
from .config import CONFIG
|
|
|
|
|
|
class NVPortal(StatePortal):
|
|
STATE_CODE = "NV"
|
|
STATE_NAME = "Nevada"
|
|
PORTAL_NAME = "SilverFlume"
|
|
PORTAL_URL = CONFIG["portal_url"]
|
|
NWRA_ADDRESS = CONFIG["nwra_address"]
|
|
NWRA_CITY = CONFIG["nwra_city"]
|
|
NWRA_STATE = CONFIG["nwra_state"]
|
|
NWRA_ZIP = CONFIG["nwra_zip"]
|
|
|
|
async def search_name(self, name: str) -> NameSearchResult:
|
|
"""Nevada business name availability.
|
|
|
|
Nevada's entity search (esos.nv.gov / SilverFlume) sits behind Imperva
|
|
Incapsula bot protection, which blocks automated/headless access (and
|
|
the residential proxy IPs are flagged too), and Nevada publishes no
|
|
public open-data API or bulk dataset we can query. So we cannot reliably
|
|
automate NV name availability the way we do TX (open-data API).
|
|
|
|
Rather than scrape a blocked portal and risk a false "available"/"taken",
|
|
we attempt one lightweight portal hit and, if we get the Incapsula
|
|
challenge or anything that is not clearly a result page, we return
|
|
available=None ("could not determine") with a note so the order flow
|
|
flags it for a manual admin check on SilverFlume. available=None is NEVER
|
|
treated as "taken", so it never wrongly blocks an order.
|
|
"""
|
|
try:
|
|
page = await self.start_browser()
|
|
await page.goto(CONFIG["name_search_url"], wait_until="domcontentloaded", timeout=45000)
|
|
await self.human_delay(2.0, 4.0)
|
|
content = await page.content()
|
|
await self.screenshot(page, f"nv_name_search_{name[:20]}")
|
|
|
|
blocked = (
|
|
"_incapsula_resource" in content.lower()
|
|
or "incapsula" in content.lower()
|
|
or len(content) < 2000 # challenge stub, not a real page
|
|
)
|
|
if blocked:
|
|
return NameSearchResult(
|
|
available=None, state_code="NV", searched_name=name,
|
|
raw_response=(
|
|
"NV portal behind Incapsula bot protection - automated "
|
|
"name check unavailable. Verify manually on SilverFlume "
|
|
"(esos.nv.gov) before filing."
|
|
),
|
|
)
|
|
|
|
return NameSearchResult(
|
|
available=None, state_code="NV", searched_name=name,
|
|
raw_response="NV name check inconclusive - verify manually on SilverFlume.",
|
|
)
|
|
except Exception as exc:
|
|
return NameSearchResult(
|
|
available=None, state_code="NV", searched_name=name,
|
|
raw_response=f"Error: {exc}",
|
|
)
|
|
|
|
async def file_llc(self, order: FormationOrder) -> FilingResult:
|
|
"""File an LLC in Nevada. Selectors pending live portal verification."""
|
|
return FilingResult(
|
|
success=False, status=FilingStatus.PENDING,
|
|
state_code="NV", entity_name=order.entity_name,
|
|
error_message=(
|
|
"NV filing adapter selectors pending verification. "
|
|
f"Admin: file manually at {CONFIG['portal_url']} — LLC formation."
|
|
),
|
|
)
|
|
|
|
async def file_corporation(self, order: FormationOrder) -> FilingResult:
|
|
"""File a corporation in Nevada. Selectors pending live portal verification."""
|
|
return FilingResult(
|
|
success=False, status=FilingStatus.PENDING,
|
|
state_code="NV", entity_name=order.entity_name,
|
|
error_message=(
|
|
"NV filing adapter selectors pending verification. "
|
|
f"Admin: file manually at {CONFIG['portal_url']} — Corp formation."
|
|
),
|
|
)
|
|
|
|
|
|
def adapter() -> NVPortal:
|
|
return NVPortal()
|