How to Build a GenAI Bluesky Bot with Langflow, TypeScript,and Node.js
Bluesky is a social network built on the AT Protocol: an open, decentralised way for building social applications. The AT Protocol is open, which means that developers can use it to build their own applications, from custom feeds, to clients, to my favorite: bots.
With the advent of generative AI, we can now build chat bots that are even smarter. They can do everything from having realistic conversations to acting independently as agents. Langflow, the drag-and-drop IDE, makes it easier to build AI agents and workflows.
Given all that, it's only natural to want to connect clever bots to social media. In this post we'll build a small TypeScript application that creates a Bluesky bot powered by Langflow.
The application
The application we’re going to build will take control of a Bluesky account, responding whenever it receives a mention or a reply. A mention is any post that contains the account's handle, whereas a reply is a post sent to a thread in which the account is participating.
We'll use the @skyware/bot package to make it easy to interact with the Bluesky API and Langflow to help generate responses to posts.
What you’ll need
To build this application, you will need the following:
- Node.js installed (I'm using the latest LTS version 22 at the time of this writing)
- A Bluesky account
- A DataStax account (sign up for a free account here)
- Depending on the flow you build, you may need API keys for the services you use
Once you’re ready, let's get building a Bluesky bot.
Setting up the application
Start by creating a new Node.js application; create a new directory called langflow-bluesky-bot and open it in your terminal.
mkdir langflow-bluesky-bot cd langflow-bluesky-bot
Initialise your new Node application with:
npm init --yes
Install the dependencies and development dependencies that you're going to use:
npm install @skyware/bot npm install typescript tsx @types/node --save-dev
Open the project in your favourite text editor, then open up package.json. I like my projects to act as ES modules. To do the same, add the following under the "main" key.
"type": "module",
Add the following scripts to package.json, too. The build script will compile the TypeScript we're going to write into JavaScript, and the start script will run that JavaScript. Finally, to make development easier, the dev script will use tsx to run the TypeScript directly and restart when changes are detected.
"scripts": { "build": "tsc", "start": "node --env-file=.env .", "dev": "tsx watch --env-file=.env ./src/index.ts" },
Speaking of TypeScript, we also need to configure the compiler. Create a file called tsconfig.json and paste in the following:
{ "compilerOptions": { "target": "es2023", "lib": [ "es2023" ], "module": "NodeNext", "sourceMap": true, "outDir": "./dist", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true } }
Create a file called .env and paste in the following:
BSKY_USERNAME= BSKY_PASSWORD= LANGFLOW_URL= LANGFLOW_TOKEN=
We will fill these variables in as we need them.
For the last bit of setup, create a src directory and a file called src/index.ts.
mkdir src touch src/index.ts
Now we're ready to start work on the code of our bot.
Coding the bot
Open src/index.ts in your editor. Start by importing the bot framework and create a bot object which will be authenticated using the account name and an app password.
import { Bot } from "@skyware/bot"; const bot = new Bot(); await bot.login({ identifier: process.env.BSKY_USERNAME!, password: process.env.BSKY_PASSWORD!, });
We haven't filled in these details in the .env file yet. So we'll need to do that before we go any further.
Bluesky account details
Log in to the account you want to power with this application. You will need to create an app password for the account. Go to your account settings, choose App passwords and then add a new password. Once you have both your account handle and app password, add them to the .env file.
We can test whether these credentials work by trying to send a post from the account. Add this code to src/index.ts.
bot.post({ text: "Hello world!", });
Now, run the bot with:
npm run dev
You should see a new post from your bot account. If you don't, check your account credentials again.
Stop the bot with Ctrl + C. Remove the code that sends the post—we don't want to keep spamming the timeline!
Listening for events
We're going to listen to the "reply" and "mention" events. You can also listen for "quote", "repost", "like", "follow", and "message" events if you want to handle other use-cases with your bot. This code sets up to listen to those two events with a single function and logs a message when the bot is ready.
bot.on("reply", respondToIncoming); bot.on("mention", respondToIncoming); console.log( `[✓] @${process.env.BSKY_USERNAME} is listening for mentions and replies.\n` );
Now we need to define the respondToIncoming
function. Let's start by seeing what happens when we get mentioned. At the top of the file, import the Post type. Then implement the respondToIncoming
function. In this case, we'll just log the author of the post and the text they sent.
// At the top add Post to the import import { Bot, Post } from "@skyware/bot"; // At the bottom async function respondToIncoming(post: Post) { console.log(`[>] @${post.author.handle}: ${post.text}\n`); }
With this code in place, start the application again with:
npm run dev
You can test that you are successfully listening for these events by sending a mention or reply from a different account.
You can see the rest of the properties of a post by checking the @skyware/bot documentation and you can explore them by logging them out to the terminal.
Polling
Note that there can be a delay between when you send a mention or reply to your bot before you see the post logged. This is because @skyware/bot polls the API every 5 seconds for updates. While there is a real-time Firehose you can subscribe to, this is overkill for this application, polling will suffice.
Now we can see posts being sent to our bot, let's build something to handle them.
Langflow
For this blog post, we'll build a simple Langflow flow to respond to incoming posts. Once you have this step working, I encourage you to play around with Langflow and see what else you can create. There are plenty of example flows you can dig into for inspiration too.
Open Langflow in the DataStax dashboard and create a new flow. For this example we'll use the Basic Prompting template.
This template takes the message that is sent to a chat input component and feeds it to a model component, OpenAI gpt-4o-mini by default. If you want to use OpenAI for this app you will need to fill in the component with an OpenAI API key. You can choose to use any of the other model components if you would prefer.
You can alter how your bot will respond by providing system instructions via the prompt component. For a simple flow like this, you might tell your model to respond like a pirate, a bored teenager, or an 80s action movie hero. Or something sensible, I guess.
The model component sends its output to a chat output component and this means that you can test your flow by chatting with it using the Playground. Once you are happy with the responses, click the API button. Here you can find the URL of your flow. Copy the URL and enter it in the .env file as the LANGFLOW_URL
.
Above the code samples in this modal you can generate an API key. Do so and then copy the API key and enter it in the .env file as the LANGFLOW_TOKEN
.
Now let's hook up the Langflow API in the code.
Working with the Langflow API
Create a function called getLangflowResponse. This function will make an HTTP POST request to the Langflow URL that we copied earlier.
The body of the request is a JSON object that defines the input_type
and output_type
as "chat" and the input_value
as the text we'll pass into this function.
To authorise the request, we pass an Authorization
header that contains the string "Bearer" and the token we generated.
Once we've made the request, we then parse the response as JSON and extract the response message from the response object.
async function getLangflowResponse(text: string) { const body = { input_type: "chat", output_type: "chat", input_value: text, }; const response = await fetch(process.env.LANGFLOW_URL!, { method: "POST", headers: { Authorization: `Bearer ${process.env.LANGFLOW_TOKEN!}`, "Content-Type": "application/json", }, body: JSON.stringify(body), }); if (!response.ok) { throw new Error("Could not respond", { cause: response }); } const data = (await response.json()) as any; return data.outputs[0].outputs[0].artifacts.message; }
Update the responseToIncoming
function to generate content using the getLangflowReponse
function, handling any errors, then post that result in response to the original post using the post.reply
function.
async function respondToIncoming(post: Post) { console.log(`[>] @${post.author.handle}: ${post.text}\n`); try { const text = await getLangflowResponse(post.text); console.log(`[<] @${process.env.BSKY_USERNAME}: ${text}\n`); await post.reply({ text }); } catch (error) { console.error(error); } }
Restart your bot process again, by stopping with Crtrl + C and then running:
npm run dev
Send yourself another mention and this time you should see responses from your bot generated by the Langflow flow.
This example bot currently has the personality of a snarky teenager. I'm not sure it’s the best bot I've built, but this is just the start.
Now that you have integrated the Langflow flow with your application code, you can experiment with Langflow and see what else you can achieve. You could experiment by building:
- a memory chatbot, so that the bot can recall previous messages
- a RAG flow using Astra DB, so that the bot can respond based on knowledge you provide to it
- an agent that has many tools at its disposal to form responses
Build more bots
Bluesky makes it really easy to build bots that are fun to interact with and Langflow makes it easy to build GenAI applications that you can plug into bots with just a few lines of code.
To get started, you can:
- get the code for this Bluesky bot on GitHub,
- sign up for Langflow,
- start building!
I'd love to hear about any bots you are building on Bluesky. Feel free to follow me on Bluesky at @philna.sh and drop me a message if you've built something cool.