# teal.fm → Bluesky Bio Updater A Cloudflare Worker that automatically updates your Bluesky bio with your currently playing track from [teal.fm](https://teal.fm), and removes it when the status expires. ## How it works On a schedule (every 1 minutes by default), the Worker: 1. Fetches your `fm.teal.alpha.actor.status` record from your PDS 2. If a track is active and not expired, appends a "Currently listening to: `{track}` by `{artist}`" line to your bio 3. If nothing is playing (or the status has expired), removes the line cleanly 4. Skips the Bluesky API call entirely if the bio hasn't changed Session tokens and the last injected bio line are cached in KV to minimise unnecessary API calls. ## Prerequisites - A Cloudflare account - A [teal.fm](https://teal.fm) account with an active PDS status record - A Bluesky account with an [app password](https://bsky.app/settings/app-passwords) ## Setup ### 1. Create a KV namespace 1. Go to **Workers & Pages → KV** in the Cloudflare dashboard 2. Click **Create a namespace**, name it `TEALFM_BIO_KV`, and save ### 2. Create the Worker 1. Go to **Workers & Pages → Create** 2. Choose **Create Worker**, give it a name, and click **Deploy** 3. Click **Edit Code** and paste in the contents of `src/index.js` 4. Before saving, update the two constants at the top of the file to point to your own PDS and DID: ```js const STATUS_URL = "https://your-pds.example.com/xrpc/com.atproto.repo.getRecord" + "?repo=did:plc:yourdidhere" + "&collection=fm.teal.alpha.actor.status" + "&rkey=self"; const BSKY_PDS = "https://your-pds.example.com"; ``` 5. Click **Deploy** ### 3. Bind the KV namespace 1. In your Worker, go to **Settings → Bindings** 2. Click **Add**, choose **KV Namespace** 3. Set the variable name to `TEALFM_BIO_KV` and select the namespace you created in step 1 4. Click **Save** ### 4. Add secrets 1. In your Worker, go to **Settings → Environment Variables** 2. Add the following as **secrets** (click **Encrypt**): | Variable name | Value | |---------------|-------| | `BSKY_IDENTIFIER` | Your Bluesky handle or DID | | `BSKY_PASSWORD` | Your Bluesky app password | 3. Click **Save** ### 5. Set the cron trigger 1. In your Worker, go to **Settings → Triggers** 2. Under **Cron Triggers**, click **Add Cron Trigger** 3. Enter `*/1 * * * *` to run every 1 minutes 4. Click **Save** ## Manual trigger You can trigger a run at any time by visiting your Worker's `/run` endpoint in a browser: ``` https://your-worker.your-subdomain.workers.dev/run ``` ## KV storage The Worker uses two KV keys: | Key | Purpose | |-----|---------| | `bsky_session` | Cached Bluesky session token (TTL: 90 min) | | `bio_last_np_line` | The exact now-playing line last injected into your bio (TTL: 1 hr) | The `bio_last_np_line` key lets the Worker reliably strip the previous line even if you manually edit your bio between runs. ## Bio format When a track is playing, the following line is appended to your existing bio, separated by a blank line: ``` Currently listening to: Track Name by Artist Name ``` When nothing is playing, the line is removed and your original bio is restored exactly. ## Configuration reference | Constant | Default | Description | |----------|---------|-------------| | `NOW_PLAYING_PREFIX` | `"Currently listening to: "` | Prefix used to identify and strip the injected line | | `KV_SESSION_KEY` | `"bsky_session"` | KV key for the cached session | | `KV_LAST_LINE_KEY` | `"bio_last_np_line"` | KV key for the last injected line | | Cron schedule | `*/2 * * * *` | How often the Worker runs (set under Settings → Triggers) |