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.
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.mdSearch 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:
Ask: "What's your home region? (e.g. California, Texas, United Kingdom)"
Run to create the workspace folder structure and register the user in the API:
text
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>
Search You.com (run all three):
text
"{region} most common backyard birds eBird species list"
"{region} uncommon seasonal rare birds eBird checklist"
"{region} rare vagrant endangered birds eBird"
From results, build a checklist with 10 common, 5 rare, 1 super rare species. Use classification signals from {baseDir}/references/you-search-queries.md.
Write the populated checklist to birdfolio/checklist.json in your workspace:
Reply with a welcome message and checklist preview:
text
🦅 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:
Save the sighting to birdfolio/lifeList.json in your workspace:
text
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:
text
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:
--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:
If isLifer is true:
"🎉 New lifer! That's your first ever [commonName]! Bird #[totalSpecies] in your Birdfolio."
If totalSpecies == 1 (this is their very first bird ever): also send their personal PWA link:
"🦅 Your Birdfolio is live! Bookmark this link to see your life list:
https://birdfolio.tonbistudio.com/app/[telegram_id]"
The telegram_id is the sender's Telegram ID from the inbound message metadata (sender_id). This is also stored in birdfolio/config.json after init.
Otherwise:
"[commonName] spotted! You've now seen [N] species in your Birdfolio."
Include: rarity badge emoji, the fun fact, checklist status (if species was on checklist, mention it).
Fallback if screenshot fails: Send a formatted text card:
Trigger: "How's my checklist?", "Birdfolio progress", "How many birds have I found?"
text
exec: python {baseDir}/scripts/get_stats.py \
--workspace <absolute path to birdfolio/ in your workspace>
Format response using checklistProgress from output:
text
📋 {region} Checklist
Common ✅✅✅⬜⬜⬜⬜⬜⬜⬜ 3/10
Rare ✅⬜⬜⬜⬜ 1/5
Super Rare ⬜ 0/1
🐦 {totalSpecies} species | {totalSightings} total sightings
📍 Last spotted: {mostRecentSighting.commonName} on {date}
🏆 Rarest find: {rarestBird.commonName} ({rarity})
Use ✅ for found, ⬜ for not found. One box per species.
Optional visual checklist card: Generate a visual HTML checklist card and screenshot it:
text
exec: python {baseDir}/scripts/generate_checklist_card.py \
--workspace <absolute path to birdfolio/ in your workspace>
Then screenshot with screenshot_card.js and send the PNG.
4. Life List View
Trigger: "Show my Birdfolio", "Show my life list"
Read birdfolio/lifeList.json from your workspace.
Group lifers by rarity (Super Rare first, then Rare, then Common). Format as a text list or generate an HTML gallery, save it to birdfolio/my-birdfolio.html in your workspace, and screenshot it.
5. Species Lookup (no logging)
Trigger: "Tell me about [species]"
Search You.com:
text
"{species} bird facts habitat range behavior diet"
"{species} bird {homeRegion} eBird frequency resident or migratory"
Return a conversational summary. Do not log a sighting or generate a card.
6. Rarest Bird
Trigger: "What's my rarest bird?", "Show my best find"
text
exec: python {baseDir}/scripts/get_stats.py \
--workspace <absolute path to birdfolio/ in your workspace>
Read rarestBird from output and reply with species name, rarity, date spotted, and region.
All Python scripts output JSON to stdout. Always pass absolute --workspace path.
screenshot_card.js uses OpenClaw's bundled playwright-core + system Chrome/Edge (no separate install needed).