Skip to content

ripe0x/ripe-bot

Repository files navigation

Ripe Bot

A multi-project on-chain Twitter / X bot. Watches Ethereum and Base for NFT mints, auction activity, secondary sales, and a few project-specific events, and posts formatted tweets with rendered token images.

It was built to operate several art collections (TBAM, Value Discovery, Ripe, and others) from a single deployment, but the architecture is generic — each project is a config object that plugs into the same indexer / poller / tweet formatter pipeline.

Why this exists

Most "tweet on mint" bots are hard-coded to one contract. This one is built around a ProjectConfig registry so a single process can:

  • Poll multiple contracts across multiple chains.
  • Use a different image renderer per project (a thumbnail API, a GIF service, whatever).
  • Format each event type with a project-specific tweet template.
  • Pull secondary-sales activity from OpenSea and merge it with on-chain mints in a single dedup'd feed.
  • Generate daily and weekly recap tweets with composite grid images.

If you want to bolt a new project on, you add one file under src/config/projects/ and register it in src/config/projects/index.ts.

Architecture

On-chain events                    OpenSea API
       │                                │
       ▼                                ▼
  chainPoller / Ponder        salesIndexer (HTTP polling)
       │                                │
       └──────────────┬─────────────────┘
                      ▼
              eventPoller (every 30s)
                      │
                      ▼
           tweetedLog (dedup, persistent)
                      │
                      ▼
       templates/tweetTemplates  +  imageService
                      │
                      ▼
              twitterClient (X API v2)

The bot runs an Express server that exposes a small HTTP API (health, stats, preview dashboard) so you can sanity-check what's about to be tweeted.

Event types

Event Source Image
mint Contract Transfer Token thumbnail / slideshow GIF
batch_mint Contract Transfer First token's GIF
auction_created Auction house Static PNG
auction_bid Auction house Static PNG
auction_settled Auction house Static PNG
sale OpenSea API Token thumbnail
batch_sale OpenSea API First token thumbnail
edition_created Edition contract Token PNG + grid composite
edition_mint Edition contract Edition grid PNG
ritual_complete Custom contract (project-specific)

Not every project enables every event type — that's controlled by EventTrackingConfig in each ProjectConfig.

Quick start

# 1. Install
npm install

# 2. Configure (see Environment Variables below)
cp .env.example .env
# fill in TWITTER_*, RPC URLs, OPENSEA_API_KEY, and the project vars you want

# 3. Generate Twitter access tokens for the posting account
npx tsx scripts/get-tokens.ts

# 4. Verify credentials
npm run verify

# 5. Run in dry-run mode (logs tweets, doesn't post)
DRY_RUN=true npm run dev

# 6. When you're happy, run for real
npm run build && npm start

The bot will:

  1. Load every project registered in src/config/projects/index.ts.
  2. Skip projects whose contract addresses aren't set in .env.
  3. Start a poller per (project, chain) pair.
  4. Serve the preview dashboard at http://localhost:3000/.

Adding a new project

  1. Create src/config/projects/your-project.ts exporting a ProjectConfig. Look at tbam.ts for a feature-complete example, or layer-burns.ts for a minimal one that only tracks a single custom event.
  2. Register it in src/config/projects/index.ts.
  3. Add the env vars your config reads to .env.example.
  4. If your project uses a new event type, add it to the EventType union in src/types.ts, the dedup tracking in src/lib/tweetedLog.ts, and a tweet template in src/templates/tweetTemplates.ts.

Environment variables

See .env.example for the full annotated list. The minimum set to post any tweet is:

Var Purpose
TWITTER_API_KEY/SECRET The X developer app (the owner pays).
TWITTER_ACCESS_TOKEN/SECRET The posting account (run get-tokens).
MAINNET_RPC_URL Any JSON-RPC endpoint.
DATA_DIR Where dedup state is persisted.

Per-project env vars (contract addresses, deployment blocks, image API URLs) are documented in .env.example.

HTTP API

The bot serves these endpoints on PORT (default 3000):

Endpoint Description
GET / Tweet preview dashboard (HTML).
GET /health Health check (poller status, last events).
GET /api/stats Tweet counts grouped by event type.
GET /config Non-secret config snapshot.
POST /api/seed Mark a list of event IDs as already tweeted (use this on first deploy to skip backfill).
GET /api/preview/:projectId/:eventType/:tokenId Render the tweet for a specific event without posting.
GET /api/image/:projectId/:tokenId Proxy the rendered image for a token.

Data persistence

The bot stores state in DATA_DIR (default ./data/). Mount this as a persistent volume in production:

File Purpose
tweeted.json Dedup log of every event → tweet ID.
twitter-handles.json ENS / Farcaster / X handle cache (skip lookups).
state-{project}-{chain}.json Last indexed block per (project, chain).
spread-state.json Spread-tracker state (Value Discovery).
weekly-snapshot.json Rolling 7-day stats for the weekly recap.

Writes are atomic (temp file + rename) so a kill mid-write won't corrupt the log. Losing tweeted.json means the bot will re-tweet historical events when it restarts — use POST /api/seed to skip them.

Running modes

npm run dev          # tsx watch, restarts on file changes
npm run dry-run      # builds then runs with DRY_RUN=true (logs only)
npm run verify       # checks Twitter credentials and exits
npm run typecheck    # tsc --noEmit
npm run gif-server   # standalone GIF renderer service (for Value Discovery)

Deployment

The included Dockerfile and fly.toml are a working Fly.io deployment, but nothing about the bot is Fly-specific — it's a stock Node.js app that wants:

  • A writable persistent volume mounted at DATA_DIR.
  • The env vars listed in .env.example.
  • One process per region (the dedup log is local).

Pick whatever runtime you prefer. If you use Fly.io, rename the app in fly.toml first.

Troubleshooting

Tweet marked as sent but not visible on X. Check the tweeted log:

cat $DATA_DIR/tweeted.json | jq '.tweetIds'

The recorded tweet ID can be opened at https://twitter.com/i/status/{id}. If the ID is missing, the event predates the audit-trail feature; if it exists but isn't on X, the tweet was deleted or the account is suspended.

Image fetch failures. The bot retries each image up to 10 times before giving up and tweeting without media. Look for [EventPoller] No image for ... retry N/10 in the logs. Usually means the project's image API is down.

Ponder not healthy. Projects that use Ponder (vs. direct RPC polling) log Ponder is not healthy, skipping poll. Check the Ponder service and its RPC endpoint.

License

MIT — see LICENSE.

About

Multi-project onchain Twitter bot for NFT mints, auctions, and secondary sales

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors