goose.art honkbot

Bluesky bot that replies to animal commands with random Flickr images

0 0 pulls 1 Updated 23h
docker pull atcr.io/goose.art/honkbot:latest
Image Size Layers
48.4 MB 1
Pushed 23h
Vulnerabilities
Loading...
Pulls
0 total
No pulls yet

Honkbot (Rust)

A single Bluesky bot that listens to the full firehose via Jetstream and replies to animal commands with random Flickr images.

Rewrite of bskybots/honkbot in Rust for reliability and performance.

A command only triggers a reply when it starts a post or is preceded by whitespace, so URLs like https://mysite.com/meow are ignored while i want to /hoot still works.

Container image

Published to ATCR — an OCI registry built on the AT Protocol:

atcr.io/goose.art/honkbot:latest

Single layer, ~48 MB, linux/amd64. .tangled/workflows/build.yaml builds and pushes it on every push to main.

Authentication

ATCR authenticates pulls, so log in first:

$ docker login atcr.io -u goose.art      # ATProto handle + app password
$ docker pull atcr.io/goose.art/honkbot:latest

Or install ATCR’s credential helper (curl -fsSL https://atcr.io/static/install.sh | sh), which uses an OAuth device flow instead of storing a password.

Anonymous pulls require the image’s storage hold to be public. This image currently lives on ATCR’s shared hold (did:plc:wnbpdx4bn5uts5yd2o2g3gnm), which is private; see Bring your own storage to publish from your own public hold.

Running the container

The image bundles the binary and the fallback_images/ tree, so it needs no volumes — only credentials through environment variables.

Variable Required Default Purpose
BSKY_HANDLE yes — Bot account handle
BSKY_PASSWORD yes — App password for the bot account
FLICKR_KEY yes — Flickr API key
BSKY_SERVICE no https://bsky.social PDS / service base URL
FALLBACK_IMAGE_DIR no /app/fallback_images Directory of bundled fallback images
FOLLOWS_ONLY no false Only reply to the bot’s followers
RUST_LOG no info Log level (debug for verbose Jetstream output)

docker run

$ docker run -d \
    --name honkbot \
    --restart unless-stopped \
    -e BSKY_HANDLE=your-bot.bsky.social \
    -e BSKY_PASSWORD=your-app-password \
    -e FLICKR_KEY=your-flickr-api-key \
    atcr.io/goose.art/honkbot:latest

Docker Compose

docker-compose.prod.yml runs the bot alongside watchtower, which polls for new images every 60 seconds and restarts the container when latest moves:

$ docker login atcr.io -u goose.art   # once, so the pull and watchtower can authenticate
$ docker compose -f docker-compose.prod.yml up -d

Credentials go in a .env file next to the compose file (see .env.example). Watchtower reads the credentials from the Docker config.json that docker login writes — the compose file mounts it into the watchtower container at /config.json, so a single login covers both. Set DOCKER_CONFIG_FILE in .env if the file lives elsewhere (NAS paths often do).

To build the image locally instead of pulling, use docker-compose.yml (docker compose up --build).

Commands

Command Animal Reply Text
/honk Goose HONK
/awoo Wolf AWOO
/baaa Goat BAAA
/bork Dog/Puppy BORK
/hiss Snake HISS
/hoot Owl HOOT
/meehh Sheep MEEHH
/meow Cat MEOW
/mumble Marmot MUMBLE
/oink Pig OINK
/ribbit Frog RIBBIT
/squee Capybara SQUEE
/yowl Bobcat YOWL

Running from source

  1. Copy .env.example to .env and fill in your credentials:

    • BSKY_HANDLE — your bot’s Bluesky handle
    • BSKY_PASSWORD — an app password for the bot account
    • FLICKR_KEY — a Flickr API key (get one here)
  2. Seed fallback images (used when Flickr is down or rate-limited):

    $ ./seed_fallback_images.sh
    

    This downloads ~12 images per category into fallback_images/. They are committed so the container image ships with them; you can also add your own .jpg/.png/.webp files.

  3. Build and run:

    $ cargo build --release
    $ ./target/release/honkbot
    

Tests live next to the code (cargo test) and cover the command-matching rules.

Features

  • Single binary handles all 13 animal commands
  • Jetstream WebSocket for efficient firehose consumption (only receives app.bsky.feed.post events)
  • Automatic reconnection with cursor tracking on disconnect
  • Bounded concurrency (10 concurrent reply tasks) to avoid flooding APIs
  • Session refresh with automatic retry on auth failures
  • Flickr fallback images — if Flickr is down or rate-limited, picks a random image from fallback_images/<command>/
  • Image resizing for images over 1MB (Bluesky blob limit)
  • Structured logging via tracing (set RUST_LOG=debug for verbose output)

Architecture

Jetstream (WSS) → match post text against triggers → spawn bounded task:
  1. Search Flickr for random animal image
  2. Download & resize if needed
  3. Upload blob to Bluesky
  4. Create reply post with image

Bring your own storage

The container image lives on ATCR, which stores image manifests as AT Protocol records in the owner’s PDS and layer blobs in a hold service (S3-compatible). Anonymous pulls are only served by holds with server.public: true; the shared hold used above is private.

To serve pulls without credentials:

  1. Deploy a hold service with server.public: true (BYOS guide).
  2. Point your ATCR profile at it by setting defaultHold in your io.atcr.sailor.profile record.
  3. Push again (git push origin main) so new blobs land on the public hold.

The OCI annotations emitted by the build (org.opencontainers.image.*, io.atcr.readme) let ATCR populate this repository page from this README automatically.