# Platform Automation

> Platform-Specific Browser Automation

- Skill: `lucadominguez/platform-automation` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lucadominguez/platform-automation`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lucadominguez/platform-automation/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: lucadominguez (https://skillmd.com/u/lucadominguez)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lucadominguez/platform-automation

---

# Platform-Specific Browser Automation

Use when automating specific platforms that have unique anti-bot measures or DOM structures. Each platform entry is a live-verified pattern.

**Support files (shared with browser-automation):**
- `references/skool-selectors.md` — Live-verified Skool.com selectors, signup flow, anti-detection notes
- `references/reddit-anti-bot.md` — Reddit's multi-layer anti-bot system: JS challenge, closed shadow DOM SSO buttons, CDP piercing via Playwright, domain strategy, rate limits
- `references/curl-cffi-tls-impersonation.md` — curl_cffi: TLS impersonation for headless HTTP (Chrome/Safari fingerprints), Reddit case study, when to skip browser entirely

## Skool.com Signup Flow (Verified May 2026)

- Landing page at `skool.com/signup` shows no form — just a "Create your community" button
- Clicking the button opens a modal with: `#first_name`, `#last_name`, `#email`, `#password`
- Submit button: `button[type='submit']` with text "Sign Up", initially `disabled` until form validates
- No Cloudflare challenge on direct connection (residential IP from WSL)
- After submit: redirects/prompts for email verification

**Skool uses React/Next.js with dynamic selectors.** CSS selectors may need updating if they change their UI. Run `headless=False` first to verify the flow.

## Reddit Anti-Bot Strategy

Reddit employs multiple detection layers:

- **JS challenge timing**: Reddit serves a challenge page before the real content. Timing matters.
- **Closed shadow DOM SSO buttons**: Web components with closed shadow roots hide interactive elements from standard DOM queries. `document.querySelectorAll`, `get_by_text`, CSS piercing `>>>`, and manual `shadowRoot` walks all fail on closed shadow DOM.
- **CDP piercing via Playwright**: The fix is CDP via `context.new_cdp_session(page)` with `DOM.performSearch({includeUserAgentShadowDOM: True})`. Always check `resultCount` before calling `getSearchResults` — a count of 0 causes "Invalid search result range".
- **Domain selection**: old.reddit blocks Chrome TLS; www.reddit.com works headless.
- **Rate limits**: ~4 attempts before rate-limiting kicks in.

**Headless detection warning**: `networkidle` hangs on Reddit — SPA maintains constant websocket/polling connections. Use `wait_until="domcontentloaded"` or `wait_until="commit"` with a fixed wait instead.

## curl_cffi TLS Impersonation

When to skip browser entirely — curl_cffi provides TLS-level impersonation for headless HTTP requests:

- Supports Chrome and Safari TLS fingerprints
- 100x lighter than launching a browser
- Use when the target site doesn't require JavaScript for the data you need

**Decision heuristic**: `re.findall(r'<form', html)` — if no forms exist in raw HTML, the login is JS-rendered and you need a real browser. If forms are present, curl_cffi can handle it.

## Live-Platform Selector Discovery

When automating a site you haven't tested live, CSS selectors are guesses. Two-phase approach:

**Phase 1: Headless reconnaissance** — Launch headless, navigate to target, dump all input/button attributes:
```python
page.goto("https://target.com/signup", wait_until="networkidle")
time.sleep(3)
inputs = page.locator("input").all()
for i in inputs:
    attrs = page.evaluate('''(el) => {
        const r = {};
        for (const a of el.attributes) r[a.name] = a.value;
        return r;
    }''', i.element_handle())
    print(attrs)
```

**Phase 2: Flow discovery** — Modern SPAs hide forms behind CTAs. Click buttons, wait for modals, re-check the DOM:
```python
page.locator("button:has-text('Get Started')").first.click()
time.sleep(3)
# Now check for inputs again — they may appear in a modal
```

## Pitfalls

- **Try HTTP before browser**: Before reaching for Playwright or browser-use, test if the target site's login is a simple form POST. Use `curl_cffi` with TLS impersonation for sites that don't require JavaScript for their login flow.
- **Headless detection by target sites**: Some sites (Reddit, Cloudflare-protected) detect and block headless Chromium even with stealth flags. Always confirm it's not an IP block first: `curl -sI https://target.com`. If curl gets 200/301 but headless Chromium gets blocked, the block is fingerprint-level. Try `--headless=new` flag, a residential proxy, or fall back to `curl_cffi`.
- **Closed shadow DOM (Reddit, modern SPAs)**: Web components with closed shadow roots hide interactive elements. Use CDP with `includeUserAgentShadowDOM: True`.
