- Dockerfile: python 3.12, Google Chrome (patchright), Xvfb, tini как PID 1 против зомби-процессов - docker-compose: volume для extra/, shm_size 2gb, порт через API_PORT - requirements: добавлены playwright и camoufox - docs: Deployment.md, README и доки актуализированы под Chrome Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
156 lines
4.9 KiB
Markdown
156 lines
4.9 KiB
Markdown
# Robotex
|
|
|
|
Browser automation API with built-in anti-bot stealth. Send a URL and a sequence of declarative actions — fill a field,
|
|
click, wait for an element — and get back the rendered page, including sites behind Cloudflare-style JS challenges.
|
|
|
|
## Status
|
|
|
|
Early MVP (v0), under active development. Single browser engine, no queue, no billing yet — see [Roadmap](#roadmap).
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Client → Caddy (TLS) → FastAPI → API-key auth → semaphore acquire
|
|
│
|
|
▼
|
|
Chrome (Patchright) context
|
|
(run actions, collect result)
|
|
│
|
|
▼
|
|
release semaphore → response
|
|
```
|
|
|
|
- **Engine**: real Google Chrome (`channel="chrome"`) driven via [Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright-python)
|
|
(undetected Playwright fork), headful under Xvfb. Firefox is not supported yet.
|
|
- **API**: FastAPI, single versioned endpoint (`POST /v1/solve`).
|
|
- **Concurrency**: bounded by an `asyncio.Semaphore` over browser contexts — no external job queue in v1. Simpler to
|
|
run, hard ceiling on throughput per instance (see Roadmap for scaling out).
|
|
- **Auth**: per-client API key (`X-API-Key` header), checked against a SQLite table. (SQLAlchemy ORM)
|
|
- **Logging**: every request's outcome (success/failure/duration) is persisted — this becomes the success-rate data used
|
|
to evaluate the service and talk to clients about reliability.
|
|
- **Proxies**: not managed by the service in v1 — the client supplies their own proxy per request if they need one.
|
|
- **Deployment**: single Docker container behind a Caddy reverse proxy — see [Docker](#docker) and
|
|
[docs/Deployment.md](docs/Deployment.md).
|
|
- **Canvas fingerprint** по дизайну генерируется каждую сессию новый.
|
|
|
|
### Project layout
|
|
|
|
```
|
|
src/
|
|
main.py # FastAPI app, POST /v1/solve
|
|
api/
|
|
actions.py # fill / click / wait_for execution
|
|
schemas.py # SolveRequest / Action models
|
|
geoip.py # proxy → locale / timezone
|
|
engine/
|
|
stealthy.py # AsyncStealthySession (Patchright + Chrome)
|
|
session.py # browser / context launch options
|
|
page_pool.py # page pool
|
|
antibot/ # anti-bot detection & bypass orchestrator (Cloudflare, reCAPTCHA)
|
|
captcha/ # captcha solvers (Turnstile, reCAPTCHA, hCaptcha)
|
|
tests/
|
|
wheels/ # whl библиотеки для оффлайн установки под Ubuntu Noble
|
|
Dockerfile
|
|
docker-compose.yml
|
|
.dockerignore
|
|
requirements.txt
|
|
env.example
|
|
```
|
|
|
|
## API (draft)
|
|
|
|
```
|
|
POST /v1/solve
|
|
X-API-Key: <key>
|
|
|
|
{
|
|
"captcha": "token",
|
|
"url": "https://example.com/login",
|
|
"actions": [
|
|
{"type": "fill", "selector": "#email", "value": "user@example.com"},
|
|
{"type": "fill", "selector": "#password", "value": "..."},
|
|
{"type": "mouse_move", "selector": "#submit"},
|
|
{"type": "click", "selector": "#submit"},
|
|
{"type": "wait_for", "selector": ".dashboard", "timeout": 30}
|
|
],
|
|
"proxy": "socks5://user:pass@host:port",
|
|
|
|
"screen": "1280x920",
|
|
"user_agent": "Mozilla/5.0 (...) Chrome/...",
|
|
"timeout": 120
|
|
}
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"ok": false,
|
|
"context": "000000",
|
|
"error": "action_error",
|
|
"html": "<html>...</html>",
|
|
"cookies": [
|
|
{
|
|
"name": "session",
|
|
"value": "..."
|
|
}
|
|
],
|
|
"actions_result": [
|
|
{
|
|
"type": "fill",
|
|
"ok": true
|
|
},
|
|
{
|
|
"type": "fill",
|
|
"ok": true
|
|
},
|
|
{
|
|
"type": "click",
|
|
"ok": true
|
|
},
|
|
{
|
|
"type": "wait_for",
|
|
"ok": false,
|
|
"error": "Element not located"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
On failure, `status` is `"error"` and `error` carries a machine-readable reason (`selector_not_found`, `timeout`,
|
|
`navigation_failed`, ...).
|
|
|
|
Fill команда включает 3 действия
|
|
|
|
1. `{"type": "mouse_move", "selector": "#email"},` - навести мышку на элемент
|
|
2. `{"type": "click"},` - клик в текущие координаты
|
|
3. `{"type": "typing", "value": "user@example.com"},` - набор текста
|
|
|
|
## Roadmap
|
|
|
|
Deliberately out of scope for v1:
|
|
|
|
- Job queue (Redis/RabbitMQ) + multiple worker VPS for horizontal scaling
|
|
- Real billing/usage plans beyond a flat API-key check
|
|
- Broader action vocabulary beyond fill/click/wait_for
|
|
- Second engine (Firefox, e.g. Camoufox) for targets where the Chrome fingerprint doesn't fit
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
python -m venv .venv
|
|
source .venv/bin/activate
|
|
make sync-offline
|
|
cd src && uvicorn main:app --reload
|
|
```
|
|
|
|
## Docker
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
```
|
|
|
|
The API listens on `http://localhost:8000`. Browser profiles (`extra/user_data_dir`) and the GeoIP cache live in the
|
|
`extra` named volume, so sessions survive container restarts. Details (Xvfb, tini, `/dev/shm`) —
|
|
[docs/Deployment.md](docs/Deployment.md).
|