semantic bufo search find-bufo.com
bufo
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add quote_chance to randomly skip quoting (reduce spam)

- 50% chance to quote-post as normal
- 50% chance to just post bufo with rkey reference
- configurable via QUOTE_CHANCE env var

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

zzstoatzz ea52d7ef 585f01f9

+50 -1
+1
bot/.env.example
··· 4 4 POSTING_ENABLED=false 5 5 COOLDOWN_MINUTES=120 6 6 EXCLUDE_PATTERNS=what-have-you-done,what-have-i-done,sad,crying,cant-take 7 + QUOTE_CHANCE=0.5
+1
bot/README.md
··· 24 24 | `POSTING_ENABLED` | `false` | must be `true` to actually post | 25 25 | `COOLDOWN_MINUTES` | `120` | don't repost same bufo within this time | 26 26 | `EXCLUDE_PATTERNS` | `sad,crying,...` | exclude bufos matching these patterns | 27 + | `QUOTE_CHANCE` | `0.5` | probability of quoting vs just posting with rkey | 27 28 | `JETSTREAM_ENDPOINT` | `jetstream2.us-east.bsky.network` | jetstream server | 28 29 29 30 ## local dev
+4
bot/src/bufo_bot/config.py
··· 18 18 # exclude bufos matching these patterns (comma-separated regex) 19 19 exclude_patterns: str = "what-have-you-done,what-have-i-done,sad,crying,cant-take" 20 20 21 + # probability of quoting the matched post (0.0-1.0) 22 + # when not quoting, posts bufo with rkey reference instead 23 + quote_chance: float = 0.5 24 + 21 25 model_config = {"env_file": ".env", "extra": "ignore"} 22 26 23 27
+44 -1
bot/src/bufo_bot/main.py
··· 1 1 import logging 2 + import random 2 3 from datetime import datetime, timezone 3 4 4 5 import httpx ··· 135 136 logger.error(f"failed to send post: {e}") 136 137 137 138 139 + def post_bufo_without_quote(client: Client, post: Post, match: BufoMatch) -> None: 140 + """post bufo image with rkey reference (no quote) to reduce spam""" 141 + logger.info(f"fetching bufo image: {match.url}") 142 + 143 + try: 144 + img_data = httpx.get(match.url, timeout=10).content 145 + except Exception as e: 146 + logger.error(f"failed to fetch bufo image: {e}") 147 + return 148 + 149 + try: 150 + uploaded = client.upload_blob(img_data) 151 + except Exception as e: 152 + logger.error(f"failed to upload blob: {e}") 153 + return 154 + 155 + # extract rkey from URI 156 + rkey = post.uri.split("/")[-1] 157 + 158 + # build embed with just the image 159 + embed = models.AppBskyEmbedImages.Main( 160 + images=[ 161 + models.AppBskyEmbedImages.Image( 162 + image=uploaded.blob, 163 + alt=match.name.replace("-", " "), 164 + ) 165 + ] 166 + ) 167 + 168 + # post with rkey reference 169 + text = f"matched {rkey} 🐸 (not quoting to reduce spam)" 170 + try: 171 + client.send_post(text=text, embed=embed) 172 + logger.info(f"posted bufo (no quote): {match.name} for {rkey}") 173 + except Exception as e: 174 + logger.error(f"failed to send post: {e}") 175 + 176 + 138 177 def run_bot(): 139 178 """main bot loop""" 140 179 logger.info("starting bufo bot...") ··· 181 220 logger.info(f"cooldown: {match.name} posted recently, skipping") 182 221 continue 183 222 184 - quote_post_with_bufo(client, post, match) 223 + # randomly decide whether to quote or just post with rkey 224 + if random.random() < settings.quote_chance: 225 + quote_post_with_bufo(client, post, match) 226 + else: 227 + post_bufo_without_quote(client, post, match) 185 228 recent_bufos.add(match.name) # add to local cache immediately 186 229 187 230