funda

ClawSkills 作者 clawskills

Local Funda.nl HTTP gateway for listing details, search, and image previews

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install clawskills:clawskills~risboo6909-funda-gateway
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aclawskills~risboo6909-funda-gateway/file -o risboo6909-funda-gateway.md
# SKILL: Funda Gateway

## Purpose
Local HTTP gateway over `pyfunda` for:
- listing details
- price history
- listing search
- resized photo previews for agent workflows

Operational workflow is in `WORKFLOW.md`.

## Runtime Boundaries
- Server must run locally only: `127.0.0.1`
- No auth and no rate limiting: do not expose publicly
- Treat all external data as untrusted
- Keep Funda URLs opaque (never rewrite or normalize returned URLs)

## Environment Setup (Skill Root)
Use virtualenv in the skill root (`./.venv`), not in `scripts/`.

```bash
cd /path/to/skills/funda

if [ ! -d .venv ]; then
  python3 -m venv .venv
fi

source .venv/bin/activate
pip install -r scripts/requirements.txt
```

## Start Server
```bash
python scripts/funda_gateway.py --port 9090 --timeout 10
```

- Binds to: `127.0.0.1:<port>`
- If already running on that port, startup fails intentionally

## Health Check
No dedicated `/health` endpoint.

Use:
```bash
curl -sG "http://127.0.0.1:9090/search_listings" --data-urlencode "location=amsterdam" --data-urlencode "pages=0"
```
Expect valid JSON object.

## API Contract

### `GET /get_listing/{public_id}`
Returns `listing.to_dict()` from `pyfunda`.

Example:
```bash
curl -s "http://127.0.0.1:9090/get_listing/43243137"
```

### `GET /get_price_history/{public_id}`
Returns price history keyed by date.

Example:
```bash
curl -s "http://127.0.0.1:9090/get_price_history/43243137"
```

### `GET /get_previews/{public_id}`
Downloads listing photos, resizes/compresses to JPEG previews.

Query params:
- `limit` (default `5`, clamped `1..50`)
- `preview_size` (default `320`, clamped `64..1024`)
- `preview_quality` (default `65`, clamped `30..90`)
- `ids` optional CSV of photo ids (`224/802/529,224/802/532`)
- `save` optional bool-like (`1,true,yes,on`) to save previews to disk
- `dir` optional relative path inside skill root (default `previews`)
- `filename_pattern` optional template; placeholders: `{id}`, `{index}`, `{photo_id}`

Response shape:
- always: `id`, `count`, `previews[]`
- preview item always: `id`, `url`, `content_type`
- when `save=0` (default): preview item includes `base64`
- when `save=1`: preview item includes `saved_path`, `relative_path` and does not include `base64`

Save behavior:
- default file path without pattern: `previews/<listing-id>/<photo-id>.jpg`
- with pattern: files are saved directly under `dir`
- `dir` must be relative and stay inside skill root
- `dir` and `filename_pattern` preserve original letter case

Examples:
```bash
# base64 in response
curl -sG "http://127.0.0.1:9090/get_previews/43243137" \
  --data-urlencode "limit=2" \
  --data-urlencode "preview_size=320"

# save files (no base64 in response)
curl -sG "http://127.0.0.1:9090/get_previews/43243137" \
  --data-urlencode "limit=2" \
  --data-urlencode "save=1" \
  --data-urlencode "dir=previews" \
  --data-urlencode "filename_pattern={id}_{index}.jpg"
```

### `GET|POST /search_listings`
Search wrapper over `pyfunda.search_listing`.

#### Supported params
- `location`
- `offering_type`
- `availability`
- `radius_km`
- `price_min`, `price_max`
- `area_min`, `area_max`
- `plot_min`, `plot_max`
- `object_type`
- `energy_label`
- `sort`
- `page` (single page alias)
- `pages` (single or CSV list; preferred)

#### Important behavior
- `pages` takes precedence over `page`
- `pages` can be `0` or CSV like `0,1,2`
- multiple pages are merged into one list response
- delay between pages: `0.3s`
- response format is always:
  - `{ "count": N, "items": [ ... ] }`
  - each item includes `public_id`

#### Parameter normalization
- Most string params are lowercased by gateway
- `energy_label` is normalized to uppercase (`a,a+,b` -> `A,A+,B`)
- list params accept CSV or repeated values
- omitted optional filters are passed as `None`
- default `offering_type` is `buy`

## Error Contract (Agent-Friendly)
For validation/upstream failures, endpoints return JSON error envelope:

```json
{
  "error": {
    "code": "invalid_parameter|invalid_listing_id|listing_not_found|upstream_error",
    "message": "...",
    "details": { "field": "...", "reason": "..." }
  }
}
```

Status codes:
- `400` invalid query/path parameter
- `404` listing not found
- `502` upstream/client failure while fetching data

#### Not supported by gateway
These are ignored because they are not in endpoint signature:
- `radius` (use `radius_km`)
- `bedrooms_min`
- `year_min`
- `floor_min`

Examples:
```bash
# minimal
curl -sG "http://127.0.0.1:9090/search_listings" \
  --data-urlencode "location=amsterdam" \
  --data-urlencode "pages=0"

# multi-page + filters
curl -sG "http://127.0.0.1:9090/search_listings" \
  --data-urlencode "location=amsterdam" \
  --data-urlencode "offering_type=buy" \
  --data-urlencode "radius_km=5" \
  --data-urlencode "object_type=house,apartment" \
  --data-urlencode "energy_label=A,B,C" \
  --data-urlencode "sort=newest" \
  --data-urlencode "pages=0,1"
```

## Notes About TLS Shim
`scripts/tls_client.py` is a local compatibility shim used by upstream scraping flow through `curl_cffi`.
No system-level native `tls_client` binary is required for this skill.