At Composio, we are super active on Twitter to post updates and interact with users. But it becomes a hectic task to post tweets and repost them on daily basis.
Therefore, I built an AI tool that can perform all these tasks for the team, while the team can focus on other important tasks.
Composio - AI Integration and tooling platform
Here’s our short intro
Composio is an open-source AI integration platform that allows you to create AI agents and integrate into your applications. Composio comes with over 150+ tools and integrations that you can use to build AI-powered applications. We provide third-party services like GitHub, Gmail, Discord, Slack, and many more.
Learn more about Composio, here
Star the Composio repository ⭐
Project Description
This project can post tweets by creating AI-generated engaging posts, repost, and quote repost tweets by other creators.
Project Workflow:
- Integrate tool - Configure the Twitter (X) Tool integration
- Define functions for actions - Create files to define the actions that will be executed by the agent, and include the corresponding functions
- Access the account - The AI tool will ask for the access for the user’s Twitter account
- Perform the actions - The bot analyzes the input it is getting and depending upon that selects the relevant action it has to perform
Tech Stack
We will use the following technologies:
- Front End - React, Vite, TailwindCSS
- Backend - FastAPI
- Authentication - Firebase
- AI Agents - Composio and CrewAI
Quick Description
- Composio - Open-source platform to build AI agents and integrate tools
- CrewAI - An open-source framework for building collaborative multiple AI bot systems
- React + Vite - React to build UI and Vite to fast develop and deploy the application
- FastAPI - Python framework for building REST APIs faster
- Firebase - Cloud platform from Google that helps to build, run, and improve apps
Prerequisites
To create this project, we need the following things:
-
OpenAI API Key
To generate OpenAI API Key, go to their site, create an account, and generate an API Key.
-
Composio API key
To create Composio API Key, Sign up here
-
Entity id
After integrating the Twitter tool, and connecting an account with it, you will find the Entity id
-
App id
To get the App id, go to API section, and run your Composio API Key. You will get your App id in the result
Let’s get started
In this project, we will
- Setup the Firebase Authentication
- Build the AI Agents using Composio and CrewAI
- Creating the backend using FastAPI
- Building the Frontend using React, Vite, and TailwindCSS
To get started, clone this repo.
To clone, run this command:
git clone https://github.com/abhishekpatil4/Tweetify.git
Go to the backend directory and run the setup.sh
file. This is the setup code.
#!/bin/bash
# Create a virtual environment
echo "Creating virtual environment..."
python3 -m venv ~/.venvs/gmail_agent
# Activate the virtual environment
echo "Activating virtual environment..."
source ~/.venvs/gmail_agent/bin/activate
# Install libraries from requirements.txt
echo "Installing libraries from requirements.txt..."
pip install -r requirements.txt
# Login to your account
echo "Login to your Composio account"
composio login
# Add calendar tool
echo "Add Twitter tool"
composio add twitter
# Copy env backup to .env file
if [ -f ".env.example" ]; then
echo "Copying .env.example to .env..."
cp .env.example .env
else
echo "No .env.example file found. Creating a new .env file..."
touch .env
fi
# Prompt user to fill the .env file
echo "Please fill in the .env file with the necessary environment variables."
echo "Setup completed successfully!"
Add the API keys in the .env
file.
To run the setup file, run this command:
chmod +x setup.sh
./setup.sh
This will create a Python virtual environment and install the necessary libraries using requirements.txt
file.
It will redirect you to Composio, log in with your account credentials.
Then you will be redirected to Twitter, login with your credentials, and give access of your Twitter account.
Once you complete integration, you can visit the Composio dashboard and monitor your integrations.
Firebase Authentication
Now, we will setup Google’s Firebase for the authentication and authorization of the users.
Start with importing the necessary libraries and specifying the user’s credentials. These credentials will be used for the authentication purpose.
import firebase_admin
from firebase_admin import credentials, auth, firestore
from pathlib import Path
import os
from dotenv import load_dotenv
load_dotenv()
creds = {
"type": os.environ.get("type"),
"project_id": os.environ.get("project_id"),
"private_key_id": os.environ.get("private_key_id"),
"private_key": os.environ.get("private_key"),
"client_email": os.environ.get("client_email"),
"client_id": os.environ.get("client_id"),
"auth_uri": os.environ.get("auth_uri"),
"token_uri": os.environ.get("token_uri"),
"auth_provider_x509_cert_url":
os.environ.get("auth_provider_x509_cert_url"),
"client_x509_cert_url": os.environ.get("client_x509_cert_url"),
}
The credentials are specified in the JSON format.
Initiate the database using the Firestore.
firebase_admin.initialize_app(credentials.Certificate(creds))
db = firestore.client()
This allows us to store the documents and manipulate them in the form of collections in Firestore.
Create Utility functions
The utility functions allows us to access the documents present in Firestore and manipulate them as per our requirements.
def get_user_by_username(username):
users_ref = db.collection('users')
query = users_ref.where('uid', '==', username).limit(1)
docs = query.get()
for doc in docs:
return doc.to_dict()
return False
The function retrieves a user document from Firestore by querying the users
collection based on the uid
field.
def update_twitter_integration_id(username: str, twitter_integration_id: str):
users_ref = db.collection('users')
query = users_ref.where('username', '==', username).limit(1)
docs = query.get()
for doc in docs:
try:
doc.reference.update(
{'twitterIntegrationId': twitter_integration_id})
print(f"Successfully updated twitterIntegrationId for user {username}")
return True
except Exception as e:
print(f"Error updating twitterIntegrationId for user {username}: {e}")
return False
print(f"User {username} not found")
return False
The function updates the twitterIntegrationId
field in a Firestore document by searching the users
collection for a matching username
.
def get_twitter_integration_id(username: str) -> str:
users_ref = db.collection('users')
query = users_ref.where('username', '==', username).limit(1)
docs = query.get()
for doc in docs:
user_data = doc.to_dict()
return user_data.get('twitterIntegrationId', '')
print(f"User {username} not found")
return ''
The function retrieves the twitterIntegrationId
field from a Firestore document by searching the users
collection for a matching username
.
def get_composio_api_key(username: str) -> str:
users_ref = db.collection('users')
query = users_ref.where('username', '==', username).limit(1)
docs = query.get()
for doc in docs:
user_data = doc.to_dict()
return user_data.get('composio_api_key', '')
print(f"User {username} not found")
return ''
The function fetches the composio_api_key
field from a Firestore document by querying the users
collection for a matching username
.
Building the AI Agent
Let’s now start building the AI agent.
Since the main functionalities in our project are posting new tweets, retweeting, and quoting tweets, we will create three separate files to handle these tasks.
Post a tweet
To generate a new tweet, post, and repost it, we will create new_tweet_repost.py
file and create AI agent in it.
Start with importing the necessary libraries and functions.
import os
from composio_crewai import Action, ComposioToolSet
from crewai import Agent, Crew, Task, Process
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
from firebase.init_class import FirebaseService
from twitter_functions import get_user_id_by_username
Load the environment variables and initiate the Firebase Service.
load_dotenv()
firebase_service = FirebaseService()
Now, create an instance of OpenAI. Here we are using the gpt-4o
model.
llm = ChatOpenAI(model="gpt-4o")
Now, we will create the agent to generate and post tweets.
def post_twitter_message(entity_id: str, message: str) -> str:
comp_api_key = firebase_service.get_composio_api_key(entity_id)
composio_toolset = ComposioToolSet(api_key=comp_api_key, entity_id=entity_id)
tools = composio_toolset.get_actions(actions=[Action.TWITTER_CREATION_OF_A_POST])
Here, we have initialized a function post_twitter_message
to generate a tweet. We have used the Composio API Key and Entity key to initiate ComposioToolSet
.
We are using the desired action from the composio_toolset
using get_actions
. TWITTER_CREATION_OF_A_POST
is the id of action that we want to perform using the tool.
Now, create the AI agent that will perform the task. We will assign a backstory, role, and other necessary parameters so that the agent can understand more about its task.
twitter_agent = Agent(
role="Twitter Agent",
goal="Create and post tweets on Twitter",
backstory="You're an AI assistant that crafts and shares tweets on Twitter.",
verbose=True,
llm=llm,
tools=tools,
allow_delegation=False,
)
Now, we will define the description of the task to be performed. This description will be used to create the assignment (Task) that the AI agent will perform.
task_description = f"""
1. Post the following message on Twitter:
"{message}"
2. Return only the tweet ID of the posted tweet.
"""
process_twitter_request = Task(
description=task_description,
agent=twitter_agent,
expected_output="The tweet ID of the posted tweet.",
)
Then, we will create the crew to define the strategy for task execution, agent collaboration, and the overall workflow.
twitter_processing_crew = Crew(
agents=[twitter_agent],
tasks=[process_twitter_request],
verbose=1,
process=Process.sequential,
)
Finally, use this created crew to execute the task and return the result.
result = twitter_processing_crew.kickoff()
return result
We will follow the same approach to create the function to repost tweets. Here’s the full code of it.
def repost_tweet(admin_entity_id: str, entity_id: str, task_description: str) -> str:
comp_api_key = firebase_service.get_composio_api_key(admin_entity_id)
composio_toolset = ComposioToolSet(api_key=comp_api_key, entity_id=entity_id)
tools = composio_toolset.get_actions(actions=[Action.TWITTER_CREATION_OF_A_POST, Action.TWITTER_CAUSES_THE_USER_IN_THE_PATH_TO_REPOST_THE_SPECIFIED_POST])
twitter_agent = Agent(
role="Twitter Agent",
goal="Repost tweets on Twitter",
backstory="You're an AI assistant that reposts tweets on Twitter, if a quote is provided, add the quote to the tweet, if no quote is provided, repost the tweet without a quote.",
verbose=True,
llm=llm,
tools=tools,
allow_delegation=False,
)
process_twitter_request = Task(
description=task_description,
agent=twitter_agent,
expected_output="Result of the repost",
)
twitter_processing_crew = Crew(
agents=[twitter_agent],
tasks=[process_twitter_request],
verbose=1,
process=Process.sequential,
)
result = twitter_processing_crew.kickoff()
return result
In this function, we have used the TWITTER_CAUSES_THE_USER_IN_THE_PATH_TO_REPOST_THE_SPECIFIED_POST
action id that will repost the tweets from the user.
Now, we will create a function to create a tweet, post it and repost it. Here we have created the function create_new_tweet_and_repost
and used the post_twitter_message
function to generate tweet and post it.
def create_new_tweet_and_repost(initial_tweet_entity_id: str, initial_tweet: str, repost_data_list: list):
tweet_id = post_twitter_message(initial_tweet_entity_id, initial_tweet)
To repost, we have to check if there’s a quote present on the tweet or not. If a quote is there, we will repost with quote otherwise simply repost it.
for repost_data in repost_data_list:
entity_id = repost_data["entity_id"]
quote = repost_data["quote"]
if quote:
task_description = f"""
Repost the tweet with ID {tweet_id} with the following quote:
"{quote}"
"""
else:
user_id = get_user_id_by_username(entity_id)
task_description = f"""
Repost the tweet with ID {tweet_id} without any quote and user ID {user_id}
"""
Finally, run the repost_tweet
function and return the results.
repost_result = repost_tweet(initial_tweet_entity_id, entity_id, task_description)
print(f"Repost result for {entity_id}: {repost_result}")
Generate Quotes
We will create a quote_generator.py
file to generate quotes to repost the tweets with quotes.
Start by importing the necessary libraries and initializing the environment variables.
import os
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
Create a client using the OpenAI API and specify the OpenAI API Key.
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
Now, create a generate_repost_quote
function and create quote
array and user_prompts
string.
def generate_repost_quote(prompt: str, tweet_content: str, number_of_quotes: int) -> list[str]:
quotes = []
user_prompt = prompt + " Tweet content: " + tweet_content
Finally, run a loop to generate the number of quotes as per number_of_quotes
passed by the user.
for _ in range(number_of_quotes):
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Generate a meaningful repost quote with hashtags and an emoji"},
{"role": "user", "content": user_prompt}
]
)
quote = completion.choices[0].message.content.strip()
quotes.append(quote)
return quotes
Using the gpt-4o-mini
model here. We have specified the different prompts for user and system in the content
. Generated quotes will get append in quotes
array and finally, the resulted quotes
array will be returned.
Reposting existing posts
We will create a respost_existing_tweet.py
file and create the function repost_tweet
to repost the existing tweets.
We will follow the same approach as we did in the repost_tweet
function in new_tweet_repost.py
file. Here’s the full code of repost_existing_tweet.py
:
import os
from composio_crewai import Action, ComposioToolSet
from crewai import Agent, Crew, Task, Process
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
from pathlib import Path
from firebase.init import get_composio_api_key
from twitter_functions import get_user_id_by_username
load_dotenv()
llm = ChatOpenAI(model="gpt-4o")
def repost_tweet(admin_entity_id: str, entity_id: str, task_description: str) -> str:
comp_api_key = get_composio_api_key(admin_entity_id)
composio_toolset = ComposioToolSet(api_key=comp_api_key, entity_id=entity_id)
tools = composio_toolset.get_actions(actions=[Action.TWITTER_CREATION_OF_A_POST, Action.TWITTER_CAUSES_THE_USER_IN_THE_PATH_TO_REPOST_THE_SPECIFIED_POST])
twitter_agent = Agent(
role="Twitter Agent",
goal="Repost tweets on Twitter",
backstory="You're an AI assistant that reposts tweets on Twitter, if a quote is provided, add the quote to the tweet, if no quote is provided, repost the tweet without a quote.",
verbose=True,
llm=llm,
tools=tools,
allow_delegation=False,
)
process_twitter_request = Task(
description=task_description,
agent=twitter_agent,
expected_output="Result of the repost",
)
twitter_processing_crew = Crew(
agents=[twitter_agent],
tasks=[process_twitter_request],
verbose=1,
process=Process.sequential,
)
result = twitter_processing_crew.kickoff()
return result
def repost_existing(admin_entity_id: str, tweet_id: str, repost_data_list: list):
for repost_data in repost_data_list:
entity_id = repost_data["entity_id"]
quote = repost_data["quote"]
if quote:
task_description = f"""
Repost the tweet with ID {tweet_id} with the following quote:
"{quote}"
"""
else:
user_id = get_user_id_by_username(entity_id)
task_description = f"""
Repost the tweet with ID {tweet_id} without any quote and user ID {user_id}
"""
repost_result = repost_tweet(admin_entity_id, entity_id, task_description)
print(f"Repost result for {entity_id}: {repost_result}")
return "Tweeting and reposting process completed."
The repost_tweet
function will repost tweets, and the repost_existing
function checks if a quote for the post is available. If a quote is available, it will repost the tweet with the quote; otherwise, it will simply repost the tweet.
Setup FastAPI Backend
Now, we will setup our backend and define the required endpoints in the main.py
file. These endpoints will be used on frontend to connect to backend.
Start with importing necessary libraries and creating a FastAPI app.
from fastapi import FastAPI, HTTPException, Request, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
from firebase.init import auth
from composio_config import createNewEntity, isEntityConnected, createTwitterIntegrationAndInitiateAdminConnection
import logging
from quote_generator import generate_repost_quote
from new_tweet_repost import create_new_tweet_and_repost
from twitter_functions import get_tweet_text_by_id, get_user_data_by_username
from repost_existing_tweet import repost_existing
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
Specify origins and middleware.
origins = [
"https://tweetify-three.vercel.app",
"http://localhost:5173",
"http://localhost",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Get the token and use it for the authentication.
def verify_token(auth_credentials: HTTPAuthorizationCredentials = Depends(
HTTPBearer())):
token = auth_credentials.credentials
try:
decoded_token = auth.verify_id_token(token)
return decoded_token
except Exception:
raise HTTPException(status_code=401, detail="Invalid or expired token")
Define Pydantic models.
class UserData(BaseModel):
admin_username: str
username: str
appType: str
class TwitterUserData(BaseModel):
username: str
class InitialiseAgentData(BaseModel):
username: str
class TweetData(BaseModel):
prompt: str
tweetContent: str
numberOfQuotes: int
class GetTweetData(BaseModel):
tweet_id: str
class TweetRequestData(BaseModel):
initial_tweet_entity_id: str
post: str
repost_data_list: list
class RepostExistingData(BaseModel):
admin_entity_id: str
tweet_id: str
repost_data_list: list
class NewIntegrationData(BaseModel):
username: str
redirectUrl: str
class NewEntityData(BaseModel):
username: str
newUserId: str
redirectUrl: str
Specify the endpoints along with the results they will return.
@app.post("/newintegration")
async def handle_request(user_data: NewIntegrationData,
decoded_token: dict = Depends(verify_token)):
user_id = decoded_token['uid']
username = user_data.username
redirectUrl = user_data.redirectUrl
res = createTwitterIntegrationAndInitiateAdminConnection(username, redirectUrl)
return res
@app.post("/newentity")
async def handle_request(user_data: NewEntityData,
decoded_token: dict = Depends(verify_token)):
user_id = decoded_token['uid']
username = user_data.username
newUserId = user_data.newUserId
redirectUrl = user_data.redirectUrl
res = createNewEntity(username, newUserId, redirectUrl)
return res
@app.post("/checkconnection")
async def handle_request(user_data: UserData,
decoded_token: dict = Depends(verify_token)):
user_id = decoded_token['uid']
admin_username = user_data.admin_username
username = user_data.username
appType = user_data.appType
res = isEntityConnected(admin_username, username, appType)
return res
@app.post("/getquotes")
async def handle_request(tweet_data: TweetData,
decoded_token: dict = Depends(verify_token)):
user_id = decoded_token['uid']
prompt = tweet_data.prompt
tweet_content = tweet_data.tweetContent
number_of_quotes = tweet_data.numberOfQuotes
res = generate_repost_quote(prompt, tweet_content, number_of_quotes)
return {"quotes": res}
@app.post("/newtweetandrepost")
async def handle_request(tweet_request_data: TweetRequestData,
decoded_token: dict = Depends(verify_token)):
initial_tweet_entity_id = tweet_request_data.initial_tweet_entity_id
initial_tweet = tweet_request_data.post
repost_data_list = tweet_request_data.repost_data_list
res = create_new_tweet_and_repost(initial_tweet_entity_id, initial_tweet, repost_data_list)
return {"result": res}
@app.post("/repostexisting")
async def handle_request(tweet_request_data: RepostExistingData,
decoded_token: dict = Depends(verify_token)):
admin_entity_id = tweet_request_data.admin_entity_id
tweet_id = tweet_request_data.tweet_id
repost_data_list = tweet_request_data.repost_data_list
res = repost_existing(admin_entity_id, tweet_id, repost_data_list)
return {"result": res}
@app.post("/gettweet")
async def handle_request(tweet_data: GetTweetData, decoded_token: dict = Depends(verify_token)):
tweet_id = tweet_data.tweet_id
try:
tweet_text = get_tweet_text_by_id(tweet_id)
return {"tweet_text": tweet_text}
except requests.exceptions.RequestException as e:
if e.response.status_code == 400:
return {"error": "An error occurred: 400 Client Error: Bad Request for url: https://api.twitter.com/2/tweets"}, 400
return {"error": str(e)}, 500
@app.post("/getusertwitterdata")
async def handle_request(user_data: TwitterUserData, decoded_token: dict = Depends(verify_token)):
username = user_data.username
try:
res = get_user_data_by_username(username)
if res is None:
return {"error": "An error occurred while fetching user data."}, 500
return res
except requests.exceptions.HTTPError as e:
if e.response.status_code == 404:
return {"error": "Username not found."}, 404
elif e.response.status_code == 429:
return {"error": "Too many requests. Please try again later."}, 429
else:
return {"error": f"HTTP error occurred: {e}"}, e.response.status_code
except requests.exceptions.RequestException as e:
return {"error": f"An error occurred: {e}"}, 500
@app.get("/")
async def handle_request():
return "ok"
Here’s what these endpoints do:
-
/newintergation
- Creates a new integration and connects the admin with the Twitter account. Return the details of the admin -
/newentity
- Creates a new entity with the provided user credentials and uses theverify_token
function to authenticate the user using the provided token. -
/checkconnection
- Checks if the connection is successfully established of provided admin credentials with the newly created entity. -
/generatequotes
- Generate multiple quotes using the provided parameters like prompts, tweet_content, number_of_quotes, and user details. -
/newtweetandrepost
- Generates a new query with the AI agent we have created and providing all the necessary inputs -
/repostexisting
- Repost an existing tweet using the entity_id, tweet_id, and repost_data_list. -
/gettweet
- Returns the content of the tweet using the id of the tweet -
/getusertwitterdata
- Returns the Twitter data of the user that is authenticated using the token -
/
- This is the test endpoint to simply checks if the backend API is successfully running or not. It will return “ok” if the API is running successfully.
At last, define the Uvicorn server.
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
To start the server, run this command:
python main.py
Your server will run on port 8000
Building Frontend
Let’s start building the frontend of our project. To keep it simple, we will not dive into each component.
Home Page
This will be our Home page component.
import Hero from "../components/Hero";
import Benefits from "../components/Benefits";
import FAQ from "../components/FAQ";
import Working from "../components/Working";
import ActionButton from "../components/ActionButton";
import DemoVideo from "../components/DemoVideo";
const Home = () => {
return <section className="bg-white dark:bg-gray-900 mt-24">
<div className="px-4 mx-auto max-w-screen-xl text-center py-16">
<Hero />
<Benefits />
<DemoVideo />
<Working />
<FAQ />
<div className="mt-32">
<ActionButton displayName={"Get started"} link={"#"} />
</div>
</div>
</section>
}
export default Home;
This will generate a Home page like this:
Once you connect your Twitter account, it will redirect you to Settings page.
Click here to see the full code of the Settings page.
On this page, users can add their Composio API key. connect their Twitter account and add more users to it.
Here’s the App.jsx
code:
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { onAuthStateChanged } from "firebase/auth";
import { auth } from "./config/firebase";
import Navbar from "./components/Navbar";
import Home from "./pages/Home";
import Footer from "./components/Footer";
import ScrollToTop from "./components/ScrollToTop";
import { useState, useEffect } from "react";
import Login from "./pages/Login";
import Settings from "./pages/Settings";
import NotFound from "./pages/NotFound";
import SkeletonLoader from "./components/SkeletonLoader";
import { SnackbarProvider } from 'notistack'
import CreatePost from "./pages/CreatePost";
import RepostExistingTweet from "./pages/Repost";
const ProtectedRoute = ({ user, children }) => {
if (!user) {
return <Navigate to="/login" replace />;
}
return children;
};
const App = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setUser(user);
setLoading(false);
});
return () => unsubscribe();
}, []);
if (loading) {
return <SkeletonLoader />
}
return (
<BrowserRouter>
<SnackbarProvider autoHideDuration={3000} preventDuplicate={true} anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}>
<Navbar user={user} />
<ScrollToTop />
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/Settings" element={
<ProtectedRoute user={user}>
<Settings user={user} />
</ProtectedRoute>
} />
<Route path="/createpost" element={
<ProtectedRoute user={user}>
<CreatePost user={user} />
</ProtectedRoute>
} />
<Route path="/repost" element={
<ProtectedRoute user={user}>
<RepostExistingTweet user={user} />
</ProtectedRoute>
} />
<Route path="/" element={<Home />} />
<Route path="*" element={<NotFound />} />
</Routes>
<Footer />
</SnackbarProvider>
</BrowserRouter>
);
}
export default App;
-
Firebase Authentication - We have used
onAuthStateChanged
to manage the authentication. It tracks the authentication status. Following this, if the user is successfully authenticated then it redirects the user to the next step i.e., Settings page. -
Routing and Redirecting - We have used
react-router-dom
to handle the routing in the project. This allows us to navigate to different routes (such as/settings
,/login
, and/
) while following the authentication status. - Skeleton Loader - We have added a Skeleton loader that will be displayed while the Firebase performs the authentication task and updates the authentication status.
Run the app
Finally, run the app using the following command:
npm run dev
This will deploy your app on the localhost.
Here’s a quick demo of the app:
You can try the live app here.
What’s Next?
In this article, we built a complete AI tool that connects to your Twitter account and can post new tweets, repost and quote repost the tweets.
Thank you for reading the article. Check out the Composio and show your support by giving us a Star⭐
Top comments (4)
This looks pretty amazing. Will try out building this app
AI agents are super amazing. Super informative share
I have faced this same problem of daily tweeting. .Will build this app for my Twitter too.
Can I build similar app for Discord as well?
Combo of Composio and CrewAI is amazing. I used them to automate my gmail. It worked amazing