···11+# Skeet.fm
22+33+A Cloudflare Worker that automatically posts your currently playing Last.fm track to Bluesky.
44+55+## Overview
66+77+This service monitors your Last.fm account for currently playing tracks and automatically creates posts on Bluesky with embedded album art and track information.
88+99+It's designed to run as a scheduled Cloudflare Worker, checking periodically for new songs and avoiding duplicate posts.
1010+1111+## Prerequisites
1212+1313+- A [Cloudflare Workers](https://workers.cloudflare.com/) account
1414+- A [Last.fm](https://www.last.fm/) account with API access
1515+- A [Bluesky](https://bsky.app/) account
1616+- Rust and `wrangler` CLI installed locally for deployment
1717+1818+## Configuration
1919+2020+### Environment Variables
2121+2222+Set the following secrets in your Cloudflare Worker:
2323+2424+| Variable | Description | Required |
2525+|----------|-------------|----------|
2626+| `LASTFM_API_KEY` | Your Last.fm API key | ✅ |
2727+| `LASTFM_USERNAME` | Your Last.fm username | ✅ |
2828+| `BSKY_USERNAME` | Your Bluesky handle (e.g., `user.bsky.social`) | ✅ |
2929+| `BSKY_PASSWORD` | Your Bluesky app password | ✅ |
3030+| `PDS_ADDRESS` | Bluesky PDS URL (defaults to `https://bsky.social`) | ❌ |
3131+3232+### KV Namespaces
3333+3434+Create two KV namespaces in Cloudflare:
3535+3636+1. `skeetfm` - Stores posted track history
3737+2. `skeetfm_blobcache` - Caches album artwork blobs
3838+3939+## Deployment
4040+4141+1. Clone the repository
4242+2. Configure your `wrangler.toml` based on what's available in this repository:
4343+4444+```toml
4545+name = "skeetfm"
4646+main = "build/index.js"
4747+compatibility_date = "2025-09-19"
4848+4949+[build]
5050+command = "cargo install -q worker-build && worker-build --release"
5151+5252+[triggers]
5353+crons = [" * * * * *"] # runs every minute, change it however you prefer
5454+5555+[observability]
5656+enabled = true
5757+5858+[[kv_namespaces]]
5959+binding = "skeetfm"
6060+id = "1daf3d0198ef400e9ad03d8495955b03"
6161+preview_id = "1daf3d0198ef400e9ad03d8495955b03"
6262+6363+[[kv_namespaces]]
6464+binding = "skeetfm_blobcache"
6565+id = "a60098e075e54b50814b2ae06f211c36"
6666+preview_id = "a60098e075e54b50814b2ae06f211c36"
6767+```
6868+6969+3. Set your secrets:
7070+```bash
7171+wrangler secret put LASTFM_API_KEY
7272+wrangler secret put LASTFM_USERNAME
7373+wrangler secret put BSKY_USERNAME
7474+wrangler secret put BSKY_PASSWORD
7575+wrangler secret put PDS_ADDRESS # Optional
7676+```
7777+7878+4. Deploy:
7979+```bash
8080+wrangler deploy
8181+```
8282+8383+## Post Format
8484+8585+Posts appear on Bluesky with:
8686+- **Text**: "🎵🎶 Now Playing 🎤🎧🎸"
8787+- **Embedded Card**:
8888+ - Album artwork thumbnail
8989+ - Song title
9090+ - Artist name
9191+ - Link to the Last.fm track page
9292+9393+## Limitations
9494+9595+- Only posts tracks that are currently playing (not recently played)
9696+- Requires active Last.fm scrobbling from your music player
9797+9898+## Security Notes
9999+100100+- Store credentials as encrypted secrets, never in code
101101+- Use app passwords for Bluesky, not your main password
102102+- Consider the privacy implications of sharing your listening habits