Bot replying w/ frames

In this guide, we’ll take a look at how to create a Farcaster bot that replies to specific keywords with a frame created on the go specifically for the reply! Here’s an example of the same:

Demo of the farcaster frames bot

For this guide, we'll go over:

  1. Creating a webhook that listens to casts
  2. Creating a bot that replies to the casts
  3. Creating frames dynamically using the neynar SDK

Before we begin, you can access the complete source code for this guide on GitHub.

Let's get started!

Setting up our server

Creating a bun server

I am using a bun server for the sake of simplicity of this guide, but you can use express, Next.js api routes or any server that you wish to use! Here's a serverless example using next.js api routes created by @df.

Create a new server by entering the following commands in your terminal:

mkdir frames-bot
cd frames-bot
bun init

We are going to need the @neynar/nodejs-sdk, so let’s install that as well:

bun add @neynar/nodejs-sdk

Once the project is created and the packages are installed, you can open it in your favourite editor and add the following in index.ts:

const server = Bun.serve({
  port: 3000,
  async fetch(req) {
    try {
      return new Response("Welcome to bun!");
    } catch (e: any) {
      return new Response(e.message, { status: 500 });
    }
  },
});

console.log(`Listening on localhost:${server.port}`);

This creates a server using bun which we will be using soon!

Finally, run the server using the following command:

bun run index.ts

Serve the app via ngrok

We’ll serve the app using ngrok to use this URL in the webhook. If you don’t already have it installed, install it from here. Once it’s installed, authenticate using your auth token and serve your app using this command:

ngrok http http://localhost:3000

Serve your app using ngrok

🚧

Free endpoints like ngrok, localtunnel, etc. can have issues because service providers start blocking events over a certain limit

Creating a webhook

We need to create a webhook on the neynar dashboard that will listen for certain words/mentions and call our server which will then reply to the cast. So, head to the neynar dashboard and go to the webhooks tab. Click on new webhook and enter the details as such:

Create a new webhook on the neynar dashboard

The target URL should be the URL you got from the ngrok command, and you can select whichever event you want to listen to. I’ve chosen to listen to all the casts with “farcasterframesbot” in it. Once you have entered all the info, click Create to create a webhook.

Creating the bot

Head over to the app section in the neynar dashboard and copy the signer uuid for your account:

Copy the signer uuid for the bot

Create a new .env file in the root of your project and add the following:

SIGNER_UUID=your_signer_uuid
NEYNAR_API_KEY=your_neynar_api_key

Add the signer UUID to the SIGNER_UUID and the neynar api key to the NEYNAR_API_KEY which you can get from the overview section of the neynar dashboard:

Copy neynar api key from the dashboard

Create a neynarClient.ts file and add the following:

import { NeynarAPIClient } from "@neynar/nodejs-sdk";

if (!process.env.NEYNAR_API_KEY) {
  throw new Error("Make sure you set NEYNAR_API_KEY in your .env file");
}

const neynarClient = new NeynarAPIClient(process.env.NEYNAR_API_KEY);

export default neynarClient;

Here we initialize the neynar client, which we can use to publish casts. Head back to index.ts and add this inside the try block:

if (!process.env.SIGNER_UUID) {
  throw new Error("Make sure you set SIGNER_UUID in your .env file");
}

const body = await req.text();
const hookData = JSON.parse(body);

const reply = await neynarClient.publishCast(
  process.env.SIGNER_UUID,
  `gm ${hookData.data.author.username}`,
  {
    replyTo: hookData.data.hash,
  }
);
console.log("reply:", reply);

You also need to import the neynar client in the index.ts file:

import neynarClient from "./neynarClient";

This will now reply to every cast that has the word “farcasterframesbot” in it with a gm. Pretty cool, right?

Let’s take this a step further and reply with a frame instead of boring texts!

Creating the frame

We’ll now generate a unique frame for every user on the fly using neynar frames. To create the frame, add the following code in the index.ts before the reply:

const creationRequest: NeynarFrameCreationRequest = {
  name: `gm ${hookData.data.author.username}`,
  pages: [
    {
      image: {
        url: "https://moralis.io/wp-content/uploads/web3wiki/638-gm/637aeda23eca28502f6d3eae_61QOyzDqTfxekyfVuvH7dO5qeRpU50X-Hs46PiZFReI.jpeg",
        aspect_ratio: "1:1",
      },
      title: "Page title",
      buttons: [],
      input: {
        text: {
          enabled: false,
        },
      },
      uuid: "gm",
      version: "vNext",
    },
  ],
};

const frame = await neynarClient.publishNeynarFrame(creationRequest);

You can edit the metadata here, I have just added a simple gm image but you can go crazy with it! For example, check out some templates in the frame studio .

Anyway, let’s continue building; you also need to add the frame as an embed in the reply body like this:

const reply = await neynarClient.publishCast(
  process.env.SIGNER_UUID,
  `gm ${hookData.data.author.username}`,
  {
    replyTo: hookData.data.hash,
    embeds: [
      {
        url: frame.link,
      },
    ],
  }
);

Putting it all together, your final index.ts file should look similar to this.

Don't forget to restart your server after making these changes!

bun run index.ts

You can now create a cast on Farcaster, and your webhook should work fine! 🥳

Conclusion

This guide taught us how to create a Farcaster bot that replies to specific keywords with a frame created on the go! If you want to look at the completed code, check out the GitHub repository.

Lastly, make sure to tag what you built with us on Farcaster by tagging @neynar, and if you have any questions, reach out to us on warpcast or Telegram!