Metadata-Version: 2.4
Name: flarebreak
Version: 0.2.0
Summary: Python wrapper for cloudflare-solver — bypass Turnstile & IUAM with ease
License: MIT
Project-URL: Homepage, https://github.com/B00H0O/cloudflare-solver
Keywords: cloudflare,turnstile,captcha,iuam,cf-clearance,scraping,bypass
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: async
Requires-Dist: aiohttp>=3.9; extra == "async"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: aiohttp>=3.9; extra == "dev"
Requires-Dist: responses; extra == "dev"
Dynamic: requires-python

# flarebreak

Python wrapper for the [cloudflare-solver](https://github.com/B00H0O/cloudflare-solver) Rust service.  
Supports **sync** (`requests`) and **async** (`aiohttp`) usage out of the box.

---

## Requirements

The Rust service must be running before you use this library:

```bash
docker build -t turnstile-solver .
docker run -d -p 407:407 turnstile-solver
```

---

## Installation

```bash
# Sync only
pip install flarebreak

# Sync + Async
pip install "flarebreak[async]"

# From source
pip install -e ".[async]"
```

---

## Quick Start

### Turnstile (sync)

```python
from flarebreak import CloudflareSolver

with CloudflareSolver() as solver:
    result = solver.turnstile(
        url="https://bypass.city",
        sitekey="0x4AAAAAAAGzw6rXeQWJ_y2P",
    )
    print(result.token)    # 1.kMLfH4VM8kCMPXvMy-QcrmU…
    print(result.elapsed)  # 2.94s
```

### IUAM / cf_clearance (sync)

```python
import requests
from flarebreak import CloudflareSolver

with CloudflareSolver() as solver:
    result = solver.iuam(url="https://nowsecure.nl")

print(result.cf_clearance)  # 155jEz2BCC8oFRCOu0x8…
print(result.user_agent)
print(result.ip)            # egress IP used during solve

# Replay the session — same IP + UA is mandatory
session = requests.Session()
session.headers.update(result.headers)   # sets Cookie + User-Agent
resp = session.get("https://nowsecure.nl")
```

### Async / parallel

```python
import asyncio
from flarebreak import AsyncCloudflareSolver

async def main():
    async with AsyncCloudflareSolver() as solver:
        results = await asyncio.gather(
            solver.turnstile(url="https://a.com", sitekey="0x..."),
            solver.turnstile(url="https://b.com", sitekey="0x..."),
            solver.iuam(url="https://c.com"),
        )
    for r in results:
        print(r)

asyncio.run(main())
```

---

## API Reference

### `CloudflareSolver(host, port, timeout, session)`

| Param | Default | Description |
|-------|---------|-------------|
| `host` | `"localhost"` | Solver service host |
| `port` | `407` | Solver service port |
| `timeout` | `60` | Seconds before giving up |
| `session` | `None` | Custom `requests.Session` |

#### `.health() → HealthResult`

```python
h = solver.health()
print(h.available, h.capacity)  # 18 / 20
print(h.healthy)                # True
```

#### `.turnstile(url, sitekey, *, cdata, action, proxy) → TurnstileResult`

| Param | Required | Description |
|-------|----------|-------------|
| `url` | ✅ | Page hosting the widget |
| `sitekey` | ✅ | `"0x4AAA…"` or `["0x…", "0x…"]` for multiple widgets |
| `cdata` | ❌ | Turnstile cData field |
| `action` | ❌ | Turnstile action field |
| `proxy` | ❌ | `"http://user:pass@host:port"` / `"socks5://…"` |

```python
result.token    # str or list[str]
result.elapsed  # "2.94s"
result.success  # True / False
```

#### `.iuam(url, *, proxy) → IUAMResult`

```python
result.cf_clearance  # extracted cookie value
result.user_agent    # must be replayed verbatim
result.ip            # egress IP (important for proxy users)
result.headers       # {"Cookie": "…", "User-Agent": "…"}
result.elapsed       # "2.87s"
result.success       # True / False
```

### `AsyncCloudflareSolver`

Same API as `CloudflareSolver`, but every method is `async`. Use as an async context manager.

---

## Exceptions

| Exception | When |
|-----------|------|
| `SolverError` | Base class for all errors |
| `TimeoutError` | Solver gave up on the challenge |
| `SolverUnavailableError` | Service is unreachable |

```python
from flarebreak import SolverError, TimeoutError, SolverUnavailableError

try:
    result = solver.turnstile(url=..., sitekey=...)
except TimeoutError:
    # retry or scale up
    pass
except SolverUnavailableError:
    # docker container not running
    pass
except SolverError as e:
    print(e)
```

---

## Custom host / scaling

```python
# Point to a different host or port
solver = CloudflareSolver(host="192.168.1.10", port=408)

# Or load-balance across scaled containers manually
import itertools
ports = itertools.cycle([407, 408, 409, 410])
solver = CloudflareSolver(port=next(ports))
```

---

## Running tests

```bash
pip install -e ".[dev]"
pytest tests/
```

---

## License

MIT
