Gemini Twitter Bot With Encore.ts

Talha AhsanTalha Ahsan
14 min read

Introduction: What is the Gemini Twitter Bot?

The Gemini Twitter Bot is an automated tool that posts tweets every hour using content generated by Google Gemini. It’s built with Encore.ts, allowing you to create and publish tweets without having to do it manually.

Why Use the Gemini Twitter Bot?

Automating your Twitter posts can save you a lot of time. If you want to keep your Twitter account active without constantly logging in to post, this bot is a great solution. Thanks to the dynamic content from Google Gemini, each tweet is unique and engaging. This bot is ideal for:

  • Developers interested in Twitter automation projects.

  • Content creators who want to simplify their social media posting.

  • Anyone with basic coding skills looking to learn about automation.

Who Can Benefit from This Bot?

This bot is designed for developers, content creators, or anyone eager to explore automation in their Twitter activities. It’s especially useful for those wanting to combine automation with creative AI content generation.

Why Choose Encore.ts?

Encore.ts is an open-source framework that helps build backends using TypeScript in a type-safe manner. It has no NPM dependencies, making it lightweight and fast. Because it’s built on Rust, Encore.ts can be up to 9 times faster than other frameworks like Express.js.

Encore.ts also simplifies app deployment. You can self-host using Docker or use the Encore Cloud Platform for automated deployments. If you're looking for a solution that focuses on performance, scalability, and type safety, Encore.ts is the way to go.

Prerequisites: What You Need to Get Started

Before diving into the Gemini Twitter Bot, make sure you have the following:

  1. Encore.ts: Install Encore by following the instructions for your operating system:

  2. Twitter Developer Account: Sign up and get your API keys at the Twitter Developer Portal.

    • You will need a Twitter API Key, API Secret, Access Token, and Access Token Secret.
  3. Google Gemini API Key: Create an account at Google Cloud Console to get your Gemini API key for tweet generation.

  4. Dependencies: Install these libraries:

    • twitter-api-v2: For interacting with the Twitter API.

    • @google/generative-ai: For generating content with Google Gemini.

Make sure everything is set up before moving on!

Step-by-Step Guide: Building Your Gemini Twitter Bot

1. Install Encore.ts

If you’re new to Encore.ts, here’s how to get started:

After installation, create your bot app with the command:

encore app create my-twitter-bot

Choose TypeScript as your language and select Empty App as the template.

2. Set Up Your Twitter and Gemini Credentials

Once your Encore app is created, securely store your credentials using Encore’s secret storage:

  • Set your Twitter API credentials:
encore secret set TwitterAPIKey --type dev,local,pr,prod
encore secret set TwitterAPISecret --type dev,local,pr,prod
encore secret set TwitterAccessToken --type dev,local,pr,prod
encore secret set TwitterAccessSecret --type dev,local,pr,prod
  • Set your Gemini API key:
encore secret set GeminiAPIKey --type dev,local,pr,prod

3. Run Your Bot Locally

Now that your app and credentials are set up, you can run your bot locally:

encore run

This command will build and start your app. You can view the bot's activity in real-time at http://localhost:4000.

WorkFlow of Bot

Image description

Setting Up the Service in Encore.ts

Encore.ts simplifies the process of defining services, whether you’re working with a single service or developing a microservices architecture.

Creating a Service

To create a service in Encore.ts, just add a file called encore.service.ts to your project directory. Here’s a simple example from this project:

import { Service } from "encore.dev/service";

export default new Service("twitterService");

In this code, we define a service named twitterService. Encore will automatically include everything in this directory and its subdirectories as part of the service. This means you don’t have to worry about configuring each file individually.

Choosing Between a Monolith and Microservices

You can start with one service and expand as your project grows. Here’s how you might approach this:

  • Single Service: In a simple setup, your twitterService could handle all Twitter-related tasks, such as posting tweets and managing scheduling.

  • Microservices: As your application expands, you might want to break down twitterService into smaller, more focused services. For instance:

    • A tweetService that specifically handles posting tweets.

    • A scheduleService that manages when posts should go live.

This flexible architecture allows you to scale your application without dealing with complicated infrastructure. You can add new services as needed, keeping your code organized and manageable.

For more details, check out the Encore documentation on services.

Integrating Twitter API and Google Gemini API

To connect your application with the Twitter API and Google Gemini API, you'll need to securely store your API keys and create the necessary clients in the same directory as your encore.service.ts file.

1. Setting Up the Twitter API

First, import the TwitterApi from the twitter-api-v2 package. Use Encore's secret management feature to access your Twitter API credentials securely:

import { TwitterApi } from "twitter-api-v2";
import { secret } from "encore.dev/config";

// Retrieve Twitter API keys from Encore's secret storage
const appKey = secret("TWITTER_API_KEY");
const appSecret = secret("TWITTER_API_SECRET");
const accessSecret = secret("TWITTER_ACCESS_SECRET");
const accessToken = secret("TWITTER_ACCESS_TOKEN");

// Create a Twitter API client
export const xRWClient = new TwitterApi({
    appKey: appKey(),
    appSecret: appSecret(),
    accessSecret: accessSecret(),
    accessToken: accessToken(),
});

// Create a Read and Write Client
export const xClient = xRWClient.readWrite;

In this code:

  • You import the necessary packages and retrieve the Twitter API keys using Encore’s secure secret management.

  • The xRWClient is created to interact with the Twitter API, while xClient is a read-write client for posting tweets and accessing user timelines.

2. Setting Up the Gemini API

Next, configure the Google Gemini API using the @google/generative-ai package:

import { GoogleGenerativeAI } from "@google/generative-ai";
import { secret } from "encore.dev/config";

// Retrieve Gemini API key from Encore's secret storage
const googleApi = secret("GEMINI_API_KEY");

// Create a Google Generative AI client
export const googleGenAI = new GoogleGenerativeAI(googleApi());

In this snippet:

  • You import the necessary packages and retrieve the Gemini API key securely.

  • The googleGenAI client is created to generate content using Google Gemini.

Organizing Your Directory Structure

Make sure that both the Twitter API and Gemini API setup code are in the same directory as your encore.service.ts file. This organization allows Encore to effectively recognize and manage your services.

By following these steps, you'll successfully integrate both the Twitter API and Google Gemini API, laying the groundwork for your Twitter bot's functionalities.

For more information, refer to Encore's documentation on secret management.

Business Logic for Tweet Generation and Posting

In this section, we’ll dive into how we can generate and post tweets using both the Twitter API and Google Gemini API. We’ll do this through a class called TwitterService, which handles everything related to tweet generation and posting.

Setting Up the TwitterService Class

Here’s what our TwitterService class looks like:

import { googleGenAI } from "../twitter/client/setup/gemini.client.setup"; // Importing Google Gemini AI client
import { xClient } from "./client/setup/twitter.client.setup"; // Importing Twitter API client

export class TwitterService {

    // Method to generate a tweet using Google Gemini AI
    private async generateTweet(): Promise<string> {
        const prompt = "Generate a funny, sarcastic tweet about crypto or coding frustrations.";

        try {
            // Specify the model for tweet generation
            const model = googleGenAI.getGenerativeModel({
                model: "gemini-1.5-flash"
            });

            // Generate content based on the prompt
            const response = await model.generateContent(prompt);
            return response.response.text() ?? 'Another day of coding!'; // Fallback message

        } catch (error: unknown) {
            console.error('Error generating tweet:', error instanceof Error ? error.message : error);
            return 'Another day of coding!'; // Fallback message on error
        }
    }

    // Method to post a dynamically generated tweet
    public async dynamicTweet(): Promise<boolean> {
        const tweet = await this.generateTweet(); // Generate the tweet

        // Validate the generated tweet
        if (!tweet || typeof tweet !== 'string' || tweet.trim() === '') {
            console.error('Generated tweet is invalid:', tweet);
            return false;
        }

        try {
            // Post the tweet using Twitter API
            const tweetResponse = await xClient.v2.tweet(tweet);
            console.log("Tweet Posted -> ", tweetResponse.data);
            return true; // Indicate success

        } catch (error: unknown) {
            console.error('Error posting tweet:', error instanceof Error ? error.message : error);
            return false; // Indicate failure
        }
    }
}

Understanding the Class Methods

  1. generateTweet Method:

    • This private method is where the magic happens for tweet generation. We start by crafting a prompt that asks for a funny or sarcastic tweet, specifically about crypto or coding frustrations.

    • We use the Google Gemini AI to generate content. If everything goes smoothly, we get a tweet back. But if there’s an issue, we’ve got a fallback—"Another day of coding!"—to keep things light.

  2. dynamicTweet Method:

    • This public method is our main action point for posting tweets. First, it calls generateTweet() to get our tweet content.

    • We check if the generated tweet is valid (not empty or null). If something seems off, we log an error and return false.

    • If everything looks good, we use the Twitter API to post the tweet. If that works, great! If not, we log the error and return false to indicate failure.

Directory Structure

Make sure the TwitterService class lives in the same directory as your encore.service.ts file. This way, Encore can easily find and manage your service.

By keeping things organized like this, we not only maintain clarity in our codebase but also make it easier to manage and expand in the future.

Creating the Twitter Controller

Now, let’s dive into setting up the TwitterController. This is where we’ll define the controller function that handles incoming requests for posting tweets. It will work closely with our TwitterService to generate and post tweets seamlessly.

Setting Up the TwitterController

Here’s what the TwitterController looks like:

import { TwitterService } from "../twitter/twitter.service"; // Importing TwitterService

export class TwitterController {

    private twitterService: TwitterService;

    constructor() {
        this.twitterService = new TwitterService(); // Instantiate TwitterService
        this.post = this.post.bind(this); // Bind the post method
    }

    // Controller method to handle posting a tweet
    public async post(): Promise<{ message: string }> {
        try {
            // Call the dynamicTweet method to post the tweet
            const response = await this.twitterService.dynamicTweet();

            // Check if the tweet posting was successful
            if (!response) {
                return { message: 'Failed to post tweet.' }; // Failure message
            }

            return { message: "Tweet Posted Successfully" }; // Success message
        } catch (error: unknown) {
            // Log error and return failure message
            console.error('Error posting tweet:', error instanceof Error ? error.message : error);
            return { message: 'Failed to post tweet.' }; // Failure message
        }
    }
}

Breaking It Down

  • Class Definition: The TwitterController class is all about managing the logic for handling tweet requests. It’s like the middleman between incoming requests and our tweeting functionality.

  • Constructor: Inside the constructor, we create an instance of TwitterService. This is our go-to for all things related to tweet generation and posting. We also bind the post method to the current context, ensuring that it always has the right this reference when called.

  • The post Method: This is where the magic happens. It’s an asynchronous method that’s ready to handle requests for posting a tweet. Here’s the flow:

    • It starts by calling this.twitterService.dynamicTweet(), which generates and posts the tweet.

    • If the tweet posting fails for any reason, we return a failure message.

    • But if it’s successful, we send back a success message to let the caller know everything went smoothly.

    • We also catch any errors that might pop up during this process, log them for debugging, and return a failure message as well.

Creating the API Endpoint for Posting Tweets

Now it’s time to set up the API endpoint that will let users post tweets effortlessly. We’ll take advantage of the api utility from Encore to define this endpoint and connect it to our TwitterController.

Implementing the API

Here’s how to do it:

import { api } from "encore.dev/api"; // Importing the API utility from Encore
import { TwitterController } from "../controller/twitter.controller"; // Importing TwitterController

const twitterController = new TwitterController(); // Instantiate TwitterController

// Define the API endpoint for posting tweets
export const postTweet = api({
    method: 'POST', // Specify the HTTP method
    path: '/api/post/x/tweet', // Define the endpoint path
    expose: true, // Set to true to allow external access
}, twitterController.post); // Connect the endpoint to the post method in TwitterController

Breaking It Down

  1. Imports:

    • First, we import the api function from encore.dev/api. This utility is our key to defining the API endpoint.

    • Next, we bring in the TwitterController, which will handle the logic for posting tweets.

  2. Controller Instance:

    • We create an instance of TwitterController, allowing us to connect the API endpoint to its methods seamlessly.
  3. API Definition:

    • The postTweet constant holds our API definition.

    • We call the api function with an object that specifies:

      • Method: We set this to POST, indicating that this endpoint is designed to handle POST requests.

      • Path: We define the endpoint as /api/post/x/tweet, which is where clients will hit to post a tweet.

      • Expose: This is set to true, meaning that anyone can access this API. If you want to keep it under wraps, you can change this to false for internal use only.

Usage Considerations

  • Exposing the API:

    • By setting expose to true, you’re making this endpoint accessible to anyone. If your goal is to restrict access (for example, if it’s just for a cron job), you should switch this to false.
  • Cron Job Integration:

    • If you’re planning to call this API from a cron job or another internal process without needing external access, simply set expose to false to limit access to that endpoint.

Setting Up a Cron Job for Tweet Posting

Let’s dive into setting up a cron job that will automatically post tweets at your chosen intervals! We’ll use the CronJob class from encore.dev/cron to handle the scheduling.

Implementing the Cron Job

Here’s how you can set it up:

import { CronJob } from "encore.dev/cron"; // Importing the CronJob class
import { postTweet } from "../apis/twitter.api"; // Importing the postTweet API

// Create a new CronJob for posting tweets
const tweetCronJob = new CronJob("Post-Tweet", {
    every: "1h", // Set to run every hour
    endpoint: postTweet // Connect the cron job to the postTweet API
});

Breaking It Down

  1. Imports:

    • We start by importing the CronJob class from encore.dev/cron. This class makes it super easy to schedule tasks.

    • Next, we import the postTweet API, which we’ll call from the cron job to actually post those tweets.

  2. Setting Up the Cron Job:

    • We create a new instance of CronJob and give it the name "Post-Tweet" for easy identification.

    • The every option is set to "1h", meaning our job will run every hour.

    • We link the cron job to the postTweet API with the endpoint property, so it knows what to execute when it’s time to post.

  3. Starting the Cron Job:

    • You can optionally start the cron job right away using tweetCronJob.start(). Depending on how your application is structured, you may want to control when this starts.

Important Considerations

  • Deployment Requirement:

    • Localhost Limitation: Keep in mind that cron jobs won’t run on localhost. To see this in action, you’ll need to deploy your application to a server that supports cron jobs.
  • Time Interval:

    • Even Distribution: When you’re setting the interval for your cron job, make sure it divides evenly into 24 hours. Valid intervals include 1h, 30m, 15m, or 5m.

    • Invalid Interval: Be careful with intervals like 7h—those don’t divide evenly into 24 and will cause issues.

Examples of Valid Time Intervals

  • Valid:

    • Every hour: "1h"

    • Every 30 minutes: "30m"

    • Every 15 minutes: "15m"

    • Every 5 minutes: "5m"

  • Invalid:

    • Every 7 hours: "7h" (this doesn’t divide evenly)

Deploying Your Twitter Bot with Encore

Ready to take your Twitter bot live? Let’s walk through the steps to deploy your bot using Encore. It’s easier than you might think!

Steps to Deploy

  1. Add Your Changes to Git:

    • First things first, let’s stage all your changes. Run this command in your terminal:
    git add .
  1. Commit Your Changes:

    • Now it’s time to commit those changes with a message that reflects what you’ve done. Use:
    git commit -m "your-commit"
  • Just replace "your-commit" with a descriptive phrase that sums up your changes. This helps you keep track of your progress!
  1. Push to Encore:

    • Finally, let’s deploy your application! Use this command to push your code to Encore:
    git push encore

What Happens Next?

  • After you run the deployment command, Encore takes over and handles the build and deployment for you. How convenient is that?

  • Keep an eye on your terminal, as it will show you the deployment progress. If anything goes wrong, Encore will provide error messages to guide you through troubleshooting.

  • Once everything is deployed successfully, your Twitter bot will be live and ready to start posting tweets on the schedule you set with the cron job!

Image description

Wrapping It Up

And that’s a wrap! You’ve successfully created and deployed your Twitter bot with Encore. If you’re eager to learn more, check out these helpful tutorials:

Feel free to explore these resources to deepen your understanding and enhance your bot!

Check Out the Source Code

If you want to see the code in action or even fork it for your own projects, here’s the link to the GitHub source code

Follow Bot on Twitter

Follow Twitter Bot account here: Bot Twitter Account

Thanks for reading, and happy coding!

0
Subscribe to my newsletter

Read articles from Talha Ahsan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Talha Ahsan
Talha Ahsan

Code by day, bugs by night. Building web apps with TypeScript, MongoDB, and plenty of coffee!