semantic bufo search find-bufo.com
bufo
README.md

bufo-bot #

bluesky bot that listens to the jetstream firehose, replies to image tags, and quote-posts matching bufo phrases.

image tags #

start a post with @find-bufo.com and attach an image or quote a post containing one. the bot compares the first available image with the Bufo collection and replies with the closest match. post records and source blobs are fetched from the author's resolved PDS through com.atproto.repo.getRecord and com.atproto.sync.getBlob, not through a Bluesky CDN derivative. attached images take precedence over quoted images. if neither post has a readable image, the bot replies with a short explanation instead. tags later in a post are treated as ordinary mentions and ignored.

opt-in #

the bot is opt-in: it only quote-posts for accounts that follow @find-bufo.com. matches from non-followers are counted in stats but never trigger a post. follow to opt in, unfollow to opt out.

how it works #

  1. connects to bluesky jetstream (firehose)
  2. handles direct image tags with visual search and a reply
  3. otherwise, checks if text contains an exact phrase matching a bufo name
  4. if matched, checks whether the author follows the bot
  5. if the author is a follower, quote-posts with the corresponding bufo image

direct image tags are explicit requests and do not require following the bot. phrase matching remains follower-only.

matching logic #

  • extracts phrase from bufo filename (e.g., bufo-let-them-eat-cake -> let them eat cake)
  • requires exact consecutive word match in post text
  • configurable minimum phrase length (default: 3 words)

configuration #

env var default description
BSKY_HANDLE find-bufo.com bluesky handle
BSKY_APP_PASSWORD required app password from bsky settings
MIN_PHRASE_WORDS 3 minimum words in phrase to match
POSTING_ENABLED false must be true to actually post
EXCLUDE_PATTERNS ... exclude bufos matching these patterns
PREFERRED_JETSTREAM required sole Jetstream host; no implicit fallback hosts
VIDEO_SERVICE_URL https://video.bsky.app HTTPS video-processing service for GIFs
STATS_PORT 8080 port for the stats HTTP server
BACKEND_URL https://find-bufo.com backend used for image proxy + bufo list

local dev #

# build
zig build

# run locally (dry run by default)
PREFERRED_JETSTREAM=stream.waow.tech ./zig-out/bin/bufo-bot

deploy #

# set secrets (once)
fly secrets set BSKY_HANDLE=find-bufo.com BSKY_APP_PASSWORD=xxxx -a bufo-bot

# deploy
fly deploy

# enable posting
fly secrets set POSTING_ENABLED=true -a bufo-bot

# check logs
fly logs -a bufo-bot

service routing #

The bot follows the same Zat discovery pattern as publex: handle → DID → DID document → PDS. Login, uploads, record writes/deletes, and service-auth requests use that account's PDS. Public post reads independently resolve the post author's PDS and send no bot credentials. Discovery failures fail the operation; they never select a provider as a fallback.

Jetstream uses only PREFERRED_JETSTREAM (the deployment config already sets stream.waow.tech). GIF processing defaults to https://video.bsky.app and can be overridden with VIDEO_SERVICE_URL. This is the only provider-specific service default. Image and text searches do not require a video service.

verification #

zig build test includes a loopback PDS fixture that exercises blob uploads, all five post variants, service auth, deletion, and expired-token handling. It uses synthetic credentials and never writes to a public repo.

To check a real public post through the bot's own code, without logging in or posting a reply:

zig build check-post -- --post at://did:plc:AUTHOR/app.bsky.feed.post/RKEY