Using AI to create Bluesky posts on-demand
I have played with the idea of putting comments on this blog, but some influential blogs like Daring Fireball and Steph Ango do not have any sort of commenting system.
So instead, I thought I would make a way to automatically write posts and post them on Bluesky. There are lots of ways to go about this, but this was the approach I took using TypeScript.
There are three main components:
- The Ghost SDK, so the blog post body could be passed to ChatGPT for context
- The OpenAI SDK, to provide generative AI
- The atproto SDK, to upload the post to Bluesky.
Putting all this into practice was extremely straightforward. I already had a server-side function to use:
const postBody = await getSinglePost(postSlug);
Then we shove this into chatGPT with a very average prompt:
const response = await client.responses.create({
model: "gpt-5-mini",
input: `Write a simple post for Bluesky .... Here is the body of the post for some context: ${postBody.html}`,
});
My first test with Bluesky resulted in plain text, which didn’t format the hyperlink to my website as a clickable link, so it needed a slightly more involved solution:
await agent.login({
identifier: process.env.BLUESKY_USERNAME!,
password: process.env.BLUESKY_PASSWORD!,
});
const rt = new RichText({
text: `${response.output_text} -- https://blog.declanwade.com/posts/${postSlug}`,
});
await rt.detectFacets(agent);
await agent.post({
$type: "app.bsky.feed.post",
text: rt.text,
facets: rt.facets,
createdAt: new Date().toISOString(),
});
These functions were all wrapped up in a simple API endpoint that I could call using a shortcut on iOS. I did try for a while to use the webhook fired by Ghost to send the post, but couldn’t get the secret to validate correctly and it was taking too long to troubleshoot.
Anyway, enjoy the slightly cringe posts on my Bluesky!