birdfolio

ClawSkills 作者 clawskills

Bird identification, life list tracking, and trading card generation. Use this skill when the user: sends a bird photo to identify, says "set up my Birdfolio" or "set my region", asks "how's my checklist", asks "Birdfolio progress", asks "how many birds have I found", asks "show my Birdfolio" or "show my life list", asks "what's my rarest bird", or asks "tell me about [bird species]". Handles everything from first-time setup through ongoing life list tracking and visual trading card generation.

安装 / 下载方式

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

Birdfolio turns bird photos into a personal life list. Users photograph birds in the wild, send the photo to you, and you identify the species with Vision. You.com provides real-time rarity and regional data. Each sighting is logged to a life list with a Pokémon-inspired rarity tier (Common / Rare / Super Rare) and gets a visual trading card sent back via Telegram.

**Data lives in:** Railway PostgreSQL (via API) + local `birdfolio/` folder (cards, birds, config)
**Scripts live in:** `{baseDir}/scripts/`
**API:** `https://api-production-d0e2.up.railway.app` (also saved to `birdfolio/config.json` after init)
**Schema reference:** `{baseDir}/references/data-schema.md`
**Search queries:** `{baseDir}/references/you-search-queries.md`

> **Note on --workspace & --api-url:** Every data script accepts `--workspace` (absolute path to `birdfolio/`) and `--api-url` (API base URL). After `init_birdfolio.py` runs, both the API URL and Telegram ID are saved to `birdfolio/config.json` and read automatically — subsequent scripts only need `--workspace`.
>
> **Telegram ID:** Read from the inbound message metadata (`sender_id`). Pass as `--telegram-id` to `init_birdfolio.py` on first setup.

---

## 1. Setup Flow

**Trigger:** User says "Set up my Birdfolio", "set my region", or sends a photo before setup exists.

**Check first:** If `birdfolio/config.json` exists in your workspace, setup is already done — skip to the relevant flow.

**Steps:**

1. Ask: *"What's your home region? (e.g. California, Texas, United Kingdom)"*

2. Run to create the workspace folder structure and register the user in the API:
   ```
   exec: python {baseDir}/scripts/init_birdfolio.py \
     --telegram-id {senderTelegramId} \
     --region "{region}" \
     --api-url "https://api-production-d0e2.up.railway.app" \
     --workspace <absolute path to birdfolio/ in your workspace>
   ```

3. Search You.com (run all three):
   ```
   "{region} most common backyard birds eBird species list"
   "{region} uncommon seasonal rare birds eBird checklist"
   "{region} rare vagrant endangered birds eBird"
   ```

4. From results, build a checklist with **10 common, 5 rare, 1 super rare** species. Use classification signals from `{baseDir}/references/you-search-queries.md`.

5. Write the populated checklist to `birdfolio/checklist.json` in your workspace:
   ```json
   {
     "{region}": {
       "common": [
         { "species": "American Robin", "slug": "american-robin", "found": false, "dateFound": null }
       ],
       "rare": [...],
       "superRare": [...]
     }
   }
   ```

6. Reply with a welcome message and checklist preview:
   ```
   🦅 Birdfolio is set up for {region}!

   Your checklist:
   Common (10):  American Robin, House Sparrow, ...
   Rare (5):     Great Blue Heron, ...
   Super Rare:   California Condor

   Send me a bird photo to start collecting!
   ```

---

## 2. Bird Identification Flow

**Trigger:** User sends a photo.

> **Getting the photo file path:** When a user sends a photo via Telegram, OpenClaw downloads it and makes the local file path available in the message attachment metadata. Capture this path — you'll need it for card generation in Step 5. If OpenClaw provides the image inline without a path, use `exec` to find the most recently downloaded file in OpenClaw's temp/media folder, or check `%APPDATA%\openclaw\media\` on Windows. Save the photo to `birdfolio/birds/{slug}-{timestamp}.jpg` for permanent storage:
> ```
> exec: copy "<attachment path>" "birdfolio/birds/<slug>-<timestamp>.jpg"
> ```

### Step 1 — Identify with Vision

The submitted photo is directly visible in your context. Analyze it (or use the `image` tool if it's not inline):
```
Identify the bird species in this photo. Return JSON only:
{
  "commonName": "...",
  "scientificName": "...",
  "confidence": "high|medium|low",
  "features": ["visible feature 1", "visible feature 2"]
}
```

**Rarity rules:**
- Bird IS on the checklist → use its tier: `common`, `rare`, or `superRare`
- Bird is NOT on the checklist → use `bonus` (shows a neutral "Bonus Find" badge, no rarity assigned)

**Confidence rules:**
- `"high"` → proceed automatically, no confirmation needed
- `"medium"` → ask: *"I think this might be a [species] — based on [features]. Does that look right to you?"* → wait for confirmation before continuing
- `"low"` → reply: *"This photo isn't clear enough for me to be confident. Could you send a clearer shot?"* → stop, do not log anything

### Step 2 — Rarity lookup

Search You.com:
```
"{commonName} {homeRegion} eBird frequency how common rare"
```

Classify using these signals:
| Tier | Script value | Signals |
|------|-------------|---------|
| Common 🟢 | `common` | "abundant", "widespread", "year-round resident", >50% of checklists |
| Rare 🟡 | `rare` | "uncommon", "seasonal", "migratory", "occasional", 5–50% of checklists |
| Super Rare 🔴 | `superRare` | "rare", "vagrant", "accidental", "endangered", <5% of checklists |

When unsure → default to **`rare`**. Always use the script value (e.g. `superRare`, not `Super Rare`) when passing `--rarity` to any script.

### Step 3 — Get a fun fact

Search You.com:
```
"{commonName} bird interesting facts habitat behavior"
```

Extract one punchy fact (1–2 sentences).

### Step 4 — Log the sighting

Save the sighting to `birdfolio/lifeList.json` in your workspace:
```
exec: python {baseDir}/scripts/log_sighting.py \
  --species "{commonName}" \
  --scientific-name "{scientificName}" \
  --rarity "{rarity}" \
  --region "{homeRegion}" \
  --notes "" \
  --workspace <absolute path to birdfolio/ in your workspace>
```

Capture from output: `isLifer`, `totalSightings`, `totalSpecies`.

### Step 5 — Update checklist

Mark the species as found in `birdfolio/checklist.json`:
```
exec: python {baseDir}/scripts/update_checklist.py \
  --species "{commonName}" \
  --region "{homeRegion}" \
  --workspace <absolute path to birdfolio/ in your workspace>
```

### Step 6 — Generate trading card

The card is a two-column design: the user's photo fills the left panel (280px), a solid dark info panel sits on the right. **Always use the user's actual submitted photo** — not a stock image.

**Step 6a — Detect bird position with Vision:**
Use the `image` tool on the submitted photo:
> "Where is the bird positioned horizontally in this photo? Give me approximately what percentage from the left edge the bird's center is (0–100)."

Convert the answer to a CSS value: `"40% center"`, `"60% center"`, `"center center"`, etc. Use this as `--object-position`.

**Step 6b — Generate the card HTML with the embedded photo:**
```
exec: python {baseDir}/scripts/generate_card.py \
  --species "{commonName}" \
  --scientific-name "{scientificName}" \
  --rarity "{rarity}" \
  --region "{homeRegion}" \
  --date "{YYYY-MM-DD}" \
  --fun-fact "{funFact}" \
  --image-path "<absolute path to submitted photo>" \
  --object-position "{objectPosition}" \
  --life-count {totalSpecies} \
  --workspace <absolute path to birdfolio/ in your workspace>
```

`--image-path` embeds the user's actual photo as base64 directly into the HTML. No separate embed step needed.

**Fallback if photo path is unavailable:** omit `--image-path` and pass `--image-url "<stock photo URL>"` instead (find a URL via You.com: `"{commonName} bird photo wildlife"`).

Capture `cardPath` from output.

**Step 6c — Screenshot, save, upload, and send:**
Run the screenshot script to render the card at 600×400 and save a PNG:
```
exec: node {baseDir}/scripts/screenshot_card.js "<cardPath>"
```
Capture `pngPath` from output.

Upload to Cloudflare R2 and get a public URL:
```
exec: python {baseDir}/scripts/upload_card.py "<pngPath>"
```
Capture `url` from output.

Update the sighting's card URL in the API (use the `id` from the log_sighting output):
```
PATCH /users/{telegram_id}/sightings/{sighting_id}/card
Body: {"card_png_url": "<url>"}
```

Send the PNG via Telegram:
```
message(action="send", media="<pngPath>")
```