This project automates the process of creating blog posts and tweeting them using Fastify, MongoDB, the Twitter API and the Bluesky API.
- Node.js (v14 or higher)
- MongoDB
- Twitter Developer Account
- Docker
- Docker Compose
- Ngrok (for local development)
-
Clone the repository:
git clone https://github.com/yourusername/twitter-blogpost-automation.git cd twitter-blogpost-automation
-
Install dependencies:
npm install
3.1. If you're willing to automate your twitter posts, create a .env
file in the root directory and add the following environment variables:
TWITTER_API_KEY=
TWITTER_API_SECRET=
TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_SECRET=
TWITTER_BEARER_TOKEN=
TWITTER_OAUTH_CLIENT_KEY=
TWITTER_OAUTH_CLIENT_SECRET=
TWITTER_OAUTH_CALLBACK_URL=
TWITTER_ACCESS_TOKEN=
You can obtain these keys and tokens from the Twitter Developer Dashboard.
3.2. If you're willing to automate your bluesky posts, create a .env
file in the root directory and add the following environment variables:
BSKY_HANDLE=
BSKY_PASSWORD=
These keys are the login information you use to sign in your account.
-
Start the server:
yarn dev
-
The server will be running at
http://localhost:8000
.
-
Ensure you have Docker and Docker Compose installed on your machine.
-
Start the services:
docker-compose up --build
-
The local MongoDB server will be running at
http://localhost:27017
.
- Navigate to
http://localhost:8000/auth/twitter
to initiate the Twitter OAuth flow. - After successful authentication, you will be redirected to the callback URL, and your access token will be
displayed in the browser. Copy it and paste in the .env
TWITTER_ACCESS_TOKEN
.
Once you've passed all required bsky envs (handle and password) you're already good to go.
To use Ngrok for local development, follow these steps:
-
Download and install Ngrok from ngrok.com.
-
Start Ngrok to create a tunnel to your local server:
ngrok http 8000
-
Ngrok will provide a public URL (e.g.,
https://abcd1234.ngrok.io
). Use this URL as the callback URL in your Twitter Developer Dashboard:TWITTER_OAUTH_CALLBACK_URL=https://abcd1234.ngrok.io/callback
-
Update your
.env
file with the new callback URL.
{
"info": {
"_postman_id": "d120d597-833b-4264-903e-03fc3ee88ee2",
"name": "Twitter/Bsky BlogPost Scheduler",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "10880151",
"_collection_link": "https://igorfelipeduca.postman.co/workspace/Duca's-Workspace~4a387d03-407e-4328-8f39-876d5a45807b/collection/10880151-d120d597-833b-4264-903e-03fc3ee88ee2?action=share&source=collection_link&creator=10880151"
},
"item": [
{
"name": "Posts",
"item": [
{
"name": "Create post",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"prompt\": \"escreva um post curto em portugues, quero que esse post tenha um formato adequado para tweets. esse post precisa falar sobre IA na vida das pessoas\",\n \"postDate\": \"2024-08-30T21:53:56.349056Z\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8000/posts/create",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["posts", "create"]
}
},
"response": []
},
{
"name": "Add post to database",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"title\": \"\",\n \"content\": \"testando\",\n \"hashtags\": [],\n \"postDate\": \"2024-08-30T21:53:56.349056Z\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8000/posts/add",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["posts", "add"]
}
},
"response": []
},
{
"name": "Retrieve all posts",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:8000/posts",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["posts"]
}
},
"response": []
},
{
"name": "Edit post",
"request": {
"method": "PUT",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"content\": \"testando api ao vivo\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8000/posts/:id",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["posts", ":id"],
"variable": [
{
"key": "id",
"value": "66d23e855cdafd5df55f19ba"
}
]
}
},
"response": []
}
]
},
{
"name": "Twitter",
"item": [
{
"name": "Tweet post",
"request": {
"method": "POST",
"header": [],
"url": {
"raw": "http://localhost:8000/posts/:id/tweet",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["posts", ":id", "tweet"],
"variable": [
{
"key": "id",
"value": "66d23e855cdafd5df55f19ba"
}
]
}
},
"response": []
}
]
},
{
"name": "Bsky",
"item": [
{
"name": "Post on Bsky",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"content\": \"golang, aka go, is considered a procedural language because it follows a step-by-step approach to break down a task into a collection of variables, procedures, and routines, or functions. this means, in go, blocks of code are organized into procedures (functions) that can be used and reused, making it easier to debug and maintain. also, go has a strong focus on concurrency, which is a big plus for procedural programming. so, yeah, that's why go is seen as a procedural language. remember, it's not just about the language, it's about how you use it.\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8000/bsky/post",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["bsky", "post"]
}
},
"response": []
}
]
},
{
"name": "AI",
"item": [
{
"name": "Generate text with GPT",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"prompt\": \"please generate a thread explaining the decentralized architecture of the bluesky network. keep the text casual and concise, using only lowercase. focus on how the architecture empowers users and promotes a more open internet. finish the thread with a question to engage followers.\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8000/gpt/generate",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["gpt", "generate"]
}
},
"response": []
}
]
},
{
"name": "Bsky Threads",
"item": [
{
"name": "Post bsky thread",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"text\": \"procedural programming is a straightforward approach where you break down a task into a series of step-by-step instructions, or procedures, making your code easy to follow and maintain. this method empowers users by giving them full control over the flow of their programs, leading to more transparent and accessible software. when code is clear and logical, it promotes collaboration and innovation, contributing to a more open and user-driven internet. what do you think are the biggest benefits of procedural programming in today's tech landscape?\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8000/bsky/thread",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["bsky", "thread"]
}
},
"response": []
},
{
"name": "Generate thread with AI",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"subject\": \"python for machine learning\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8000/bsky/thread/generate",
"protocol": "http",
"host": ["localhost"],
"port": "8000",
"path": ["bsky", "thread", "generate"]
}
},
"response": []
}
]
},
{
"name": "Hello",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:8000",
"protocol": "http",
"host": ["localhost"],
"port": "8000"
}
},
"response": []
}
],
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"packages": {},
"exec": [""]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"packages": {},
"exec": [""]
}
}
]
}
This project is licensed under the MIT License.