developers

How to Make a Twitter Bot in Python using Tweepy

A reply-to-mention Twitter bot that can reply to every tweet where it got mentioned with a specific keyword with an auto-generated image.

Jul 19, 202119 min read

TL;DR: This blog post is aimed to demonstrate how to make a custom Twitter bot in Python using the official Twitter API. The bot will reply to every tweet in which it got mentioned with a specific keyword. The reply will be in the form of an image with a quote written on it.

Source code of this application is available in this GitHub repository

Introduction

In this article, you'll learn how to make your own Twitter bot in Python using Tweepy, a Python library for accessing the official Twitter API.

You will be creating a Reply to mentions bot, which will send a reply to everyone's tweet who has mentioned it with a specific keyword.

The reply will be in the form of an image that you will generate and put some text over it. This text will be a quote that you will fetch from a third-party API.

Here's how it will look like:

Example_Tweet

Prerequisites

To follow along with this tutorial, make sure you have:

An AWS account

You are going to deploy the final application to AWS Elastic Beanstalk so make sure you are signed up on AWS.

Twitter API authentication credentials

To enable your bot to interact with Twitter, you first have to sign up to a Twitter developer account and create an application for which Twitter will grant you access (There is a detailed explanation of this step in the next section).

Python 3

At the time of writing this article, the latest version is Python 3.9, but it is always recommended to choose a version that is one point revision behind the latest one so that you do not face any compatibility issues with third-party modules. For this tutorial, you can go with Python 3.8.

Installed these external Python libraries on your local environment

  1. Tweepy — To interact with Twitter API
  2. Pillow — To create an image and to add texts over it
  3. Requests — To make HTTP requests to the random quote generator API
  4. APScheduler — To schedule your job periodically
  5. Flask — To create a web app for deploying your application on Elastic Beanstalk

Rest all other libraries that you'll see in this project are part of Python's standard library, so you do not need to install them.

Twitter API Authentication Credentials

Any request that is accessing the official Twitter API requires OAuth for authenticating. That's why you need to create those required credentials to be able to use the API. These credentials include:

  1. A consumer key
  2. A consumer secret
  3. An access token
  4. An access secret

You need to follow the steps below to create your credentials once you have signed up to Twitter:

Step 1: Apply for a Twitter Developer Account

Go to the Twitter developer platform to apply for a developer account.

Twitter will ask for some information about how you're planning to use the developer account. So, you have to specify the use case for your application.

Try to be as specific as possible about your intended use for faster and better chances of getting approval.

Once you submit your application, you'll land on this screen :

Application_Recieved.jpg

Step 2: Create an Application

You'll receive the confirmation back within a week. Once your Twitter developer account access gets approved, create a project on the Twitter developer portal dashboard.

Portal

You have to do this process because Twitter permits authentication credentials only for apps. An app can be defined as any tool that uses the Twitter API. You need to provide the following information about your project:

  • Project name: a name to identify your project (such as Reply-To-Mention-Bot)

Name of the project

  • Category: Select the category to which your project belongs. In this case, choose "Making a bot."

Category

  • Description: The purpose of your project or how users will use your app (such as this app is used to automatically respond to tweets)

Description

  • Name of app: Finally, enter the name of your app

App Name

Step 3: Create the Authentication Credentials

To create your authentication credentials, firstly, go to your Twitter apps section. Here you'll find the "Keys and Tokens" tab; clicking this will take you to another page where you can generate the credentials.

KEYS.png

After generating the credentials, save them to your local machine to later use in your code. Inside your project folder, create a new file called

credentials.py
and store these four keys in the key-value format as shown below:

access_token="XXXXXXX"
access_token_secret="XXXXXXXX"
API_key="XXXXXXX"
API_secret_key="XXXXXXXX"

You can even test the credentials to check if everything is working as expected using the following code snippet:

import tweepy

# Authenticate to Twitter
auth = tweepy.OAuthHandler("CONSUMER_KEY", "CONSUMER_SECRET")
auth.set_access_token("ACCESS_TOKEN", "ACCESS_SECRET")
api = tweepy.API(auth)

try:
    api.verify_credentials()
    print("Authentication Successful")
except:
    print("Authentication Error")

If everything is correct, you should be able to see a response saying "Authentication Successful".

Understanding Tweepy

Tweepy is an open-sourced, easy-to-use Python library for accessing the Twitter API. It gives you an interface to access the API from your Python application.

To install the latest version of Tweepy, type the following command in your console:

pip install tweepy

Alternatively, you can also install it from the GitHub repository.

pip install git+https://github.com/tweepy/tweepy.git

Let's now understand some of its basic functionalities:

OAuth

Tweepy provides an

OAuthHandler
class that takes care of the OAuth required by Twitter to authenticate API calls. The code you just saw above depicts the OAuth functionality by Tweepy.

Twitter API wrapper

Tweepy also provides an API class for accessing the Twitter RESTful API methods, which you can use to access various Twitter functionalities. You can find those methods here and the most commonly used are listed below:

  • Methods for tweets
  • Methods for users
  • Methods for user timelines
  • Methods for trends
  • Methods for likes

Models

When you call any of the API methods that you just saw above, you'll get a Tweepy model class instance in response. This will contain the response returned from Twitter. For example:

user = api.get_user('apoorv__tyagi')

This returns a User model which contains the data which you can further use in your application. For example:

python
print(user.screen_name) #User Name
print(user.followers_count) #User Follower Count

Fetching the Quote

Now you will begin with the first step towards building your Bot. As stated above, when someone mentions your bot, it will reply to them with an image having a quote written on it.

So to fetch the quote in the first place, you will be calling a Random quote generator API To do that, create a Python file

tweet_reply.py
and make a new method inside it that will make an HTTP request to this API endpoint and get a quote in response. For this, you can use Python's
requests
library.

The requests library is used to make HTTP requests in Python. It abstracts the complexities of making requests behind a simple API so that you can only focus on interacting with services and consuming data in your application.

def get_quote():
    URL = "https://api.quotable.io/random"
    try:
        response = requests.get(URL)
    except:
        print("Error while calling API...")

The response looks like this:

{
   "_id": "FGX8aUpiiS5z",
   "tags": [
      "famous-quotes"
   ],
   "content": "I do not believe in a fate that falls on men however they act, but I do believe in a fate that falls on them unless they act.",
   "author": "Buddha",
   "authorSlug": "buddha",
   "length": 125
}

The API returns a JSON response, so to parse it, you can use the

JSON
library. Json is a part of Python's standard library, so you can directly import it using:
import json
.

From the response, you will only need the content and the author, so you will make your function return only those values. Here's how the complete function will look like:

def get_quote():
    URL = "https://api.quotable.io/random"
    try:
        response = requests.get(URL)
    except:
        print("Error while calling API...")

    res = json.loads(response.text)
    return res['content'] + "-" + res['author']

Generating Image

You've got your text. Now you need to create an image and put this text over it.

Whenever you need to deal with any image-related tasks in python, always first look for the

Pillow
library. Pillow is Python's imaging library which gives powerful image processing capabilities to the Python interpreter along with providing extensive file format support.

Create a seperate file, name it

Wallpaper.py
and add one function that will accept the quote as a string in its parameter and will initialize all the required variables to generate an image:

def get_image(quote):
    image = Image.new('RGB', (800, 500), color=(0, 0, 0))
    font = ImageFont.truetype("Arial.ttf", 40)
    text_color = (200, 200, 200)
    text_start_height = 100
    write_text_on_image(image, quote, font, text_color, text_start_height)
    image.save('created_image.png')

Let's understand how this function works:

  • Image.new()
    method creates a new image using the provided mode & size. The first parameter is the mode to be used for creating the new image. (It could be RGB, RGBA). The second parameter is size. Size is provided in pixels as a tuple of width & height. The last parameter is the color, i.e., what color to use for the image background (Default is black).
  • ImageFont.truetype()
    creates a font object. This function loads a font object from the given font file with the specified font size. In this case, you will be using "Arial", you can also use any other font of your choice by downloading it from here. Make sure the font file has .ttf (TrueType font) file extension, and you save it inside the root directory of your project.
  • text_color
    &
    text_start_height
    as the name itself depicts, these are the color and the start height of the text, respectively. RGB(200,200,200) is a "Light Grey" color that can work well over your black color image.
  • You call the
    write_text_on_image()
    function, which will put this text over the image using these variables.
  • image.save()
    will finally save the image as
    created_image.png
    file in your root folder. If there is an already existing file with this name, it will automatically replace it with the new one.
def write_text_on_image(image, text, font, text_color, text_start_height):
    draw = ImageDraw.Draw(image)
    image_width, image_height = image.size
    y_text = text_start_height
    lines = textwrap.wrap(text, width=40)
    for line in lines:
        line_width, line_height = font.getsize(line)
        draw.text(((image_width - line_width) / 2, y_text),line, font=font, fill=text_color)
        y_text += line_height

This is the next function in the same file,

Wallpaper.py
, where you will be putting text over the image. Let's understand the working of this function as well :

  • The
    ImageDraw
    module is used to create 2D image objects.
  • textwrap.wrap()
    wraps the single paragraph in text (a string), so every line is at most 'width'(=40) characters long. It returns a list of output lines.
  • draw.text()
    Draws the string at the given position. The complete syntax and list of parameters it accepts are defined below:
    ImageDraw.Draw.text(XY, text, fill, font)

Parameters:

  1. XY — Top left corner of the text.
  2. text — Text to be drawn.
  3. fill — Color to use for the text.
  4. font — An ImageFont instance.

In the end, here's how your

Wallpaper.py
will look like:

from PIL import Image, ImageDraw, ImageFont
import textwrap

def get_wallpaper(quote):
    # image_width
    image = Image.new('RGB', (800, 400), color=(0, 0, 0))
    font = ImageFont.truetype("Arial.ttf", 40)
    text1 = quote
    text_color = (200, 200, 200)
    text_start_height = 100
    draw_text_on_image(image, text1, font, text_color, text_start_height)
    image.save('created_image.png')

def draw_text_on_image(image, text, font, text_color, text_start_height):
    draw = ImageDraw.Draw(image)
    image_width, image_height = image.size
    y_text = text_start_height
    lines = textwrap.wrap(text, width=40)
    for line in lines:
        line_width, line_height = font.getsize(line)
        draw.text(((image_width - line_width) / 2, y_text),line, font=font, fill=text_color)
        y_text += line_height

Replying to the Mentions by Periodically Checking for Tweets

You have got the quote as well as an image that uses it. Now, the only thing left is to check for such tweets in which you are mentioned. Here, apart from only checking for mentions, you will also look for a specific keyword or a hashtag in it.

If a particular keyword/hashtag is found in the tweet, you will like and send a reply to that particular tweet.

In this case, you can go with "#qod" (short for "Quote Of the Day") as your keyword.

Coming back inside the

tweet_reply.py
file, here's the function to achieve this:

def respondToTweet(last_id):
    mentions = api.mentions_timeline(last_id, tweet_mode='extended')
    if len(mentions) == 0:
        return

    for mention in reversed(mentions):
        new_id = mention.id

        if '#qod' in mention.full_text.lower():
            try:
                tweet = get_quote()
                Wallpaper.get_wallpaper(tweet)

                media = api.media_upload("created_image.png")

                api.create_favorite(mention.id)
                api.update_status('@' + mention.user.screen_name + " Here's your Quote", 
                      mention.id, media_ids=[media.media_id])
            except:
                print("Already replied to {}".format(mention.id))
  • respondToTweet()
    function takes the last_id as its only parameter. This variable stores the last tweet to which you have responded and is used to only fetch mentions created after those already processed. So for the first time when you call the function, you will set its value as 0, and on the subsequent call, you will keep updating this value.
  • mentions_timeline()
    function in the Tweepy module is used to get the most recent tweets. The first parameter, i.e., the last_id, is used to fetch only the tweets newer than this specified ID. By default, it returns 20 most recent tweets. tweet_mode='extended' is used to get the string that contains the entire untruncated text of the Tweet. The default value is "compat" which returns text truncated to 140 characters only.

You then loop through all of those tweets in reversed order, i.e., oldest tweet first, and for each tweet mentioning you, the tweet is liked using

create_favorite()
, which just takes the tweet_id as its parameter.

The reply to this tweet is sent using

update_status()
which takes the Twitter handle for the original tweet author (You pass it using mention.user.screen_name), text content(if any), original tweet id on which you are replying, and finally the list of media which in your case is the single image you previously generated.

Saving Tweet ID to Avoid Repetition

You need to make sure to avoid replying to the same tweet again. For that, you will simply store the tweet Id to which you have last replied inside a text file

tweet_ID.txt
, and you will only check for the tweets that are posted after that. This will be automatically handled by the method
mentions_timeline()
as tweet Ids are time sortable.

And now, instead of passing the last_id yourself to the

respondToTweet()
, you will pass the file containing this last id, and your function will fetch the Id from the file, and at the end, the file will get updated with the latest one.

Here's how the final version of the

respondToTweet()
function will look like:

def respondToTweet(file):
    last_id = get_last_tweet(file)
    mentions = api.mentions_timeline(last_id, tweet_mode='extended')
    if len(mentions) == 0:
        return

    for mention in reversed(mentions):
        new_id = mention.id
        if '#qod' in mention.full_text.lower():
            try:
                tweet = get_quote()
                Wallpaper.get_wallpaper(tweet)
                media = api.media_upload("created_image.png")
                api.create_favorite(mention.id)
                api.update_status('@' + mention.user.screen_name + " Here's your Quote", 
                      mention.id, media_ids=[media.media_id])
            except:
                logger.info("Already replied to {}".format(mention.id))

    put_last_tweet(file, new_id)

You will observe that two new utility functions are also added here which are

get_last_tweet()
and
put_last_tweet()
.

get_last_tweet()
takes a file name as a parameter and will simply fetch the Id stored inside this text file, whereas
put_last_tweet()
along with the file name will take the latest tweet_id and update the file with this latest Id.

After putting everything together, here's how your complete

tweet_reply.py
will look like:

import tweepy
import json
import requests
import logging

import Wallpaper
import credentials

consumer_key = credentials.API_key
consumer_secret_key = credentials.API_secret_key
access_token = credentials.access_token
access_token_secret = credentials.access_token_secret

auth = tweepy.OAuthHandler(consumer_key, consumer_secret_key)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)

# For adding logs in application
logger = logging.getLogger()
logging.basicConfig(level=logging.INFO)
logger.setLevel(logging.INFO)

def get_quote():
    url = "https://api.quotable.io/random"

    try:
        response = requests.get(url)
    except:
        logger.info("Error while calling API...")
    res = json.loads(response.text)
    print(res)
    return res['content'] + "-" + res['author']

def get_last_tweet(file):
    f = open(file, 'r')
    lastId = int(f.read().strip())
    f.close()
    return lastId

def put_last_tweet(file, Id):
    f = open(file, 'w')
    f.write(str(Id))
    f.close()
    logger.info("Updated the file with the latest tweet Id")
    return

def respondToTweet(file='tweet_ID.txt'):
    last_id = get_last_tweet(file)
    mentions = api.mentions_timeline(last_id, tweet_mode='extended')
    if len(mentions) == 0:
        return

    new_id = 0
    logger.info("someone mentioned me...")

    for mention in reversed(mentions):
        logger.info(str(mention.id) + '-' + mention.full_text)
        new_id = mention.id

        if '#qod' in mention.full_text.lower():
            logger.info("Responding back with QOD to -{}".format(mention.id))
            try:
                tweet = get_quote()
                Wallpaper.get_wallpaper(tweet)

                media = api.media_upload("created_image.png")

                logger.info("liking and replying to tweet")

                api.create_favorite(mention.id)
                api.update_status('@' + mention.user.screen_name + " Here's your Quote", mention.id,
                                  media_ids=[media.media_id])
            except:
                logger.info("Already replied to {}".format(mention.id))

    put_last_tweet(file, new_id)

if __name__=="__main__":
    respondToTweet()

Deploying Bot to the Server

The final step would be to deploy your code to a server. In this section, you'll learn how you can deploy a Python application to AWS Elastic Beanstalk.

With Elastic Beanstalk, you can quickly deploy and manage applications in the AWS Cloud without worrying about the infrastructure that runs those applications. It reduces management complexity without restricting choice or control. You simply upload your application, and Elastic Beanstalk automatically handles the details of capacity provisioning, load balancing, scaling, and application health monitoring.

Here's how you will proceed:

  • Create an AWS Elastic Beanstalk environment for a Python application
  • Create a simple Flask Application for your Twitter bot
  • Upload your Flask App on AWS Elastic Beanstalk
  • Debug errors via logs

Creating Elastic Beanstalk environment

Once you are signed in to your AWS account, go to the search panel at the top, type & select "Elastic Beanstalk," and click create a new application on the top right.

Search Beanstalk

It will ask for your:

  1. Application name
  2. Application tags(not mandatory)
  3. Platform
  4. Application code

For tags, you can add up to 50 tags for the resources of your AWS Elastic Beanstalk applications. Tags can help you categorize resources. If you're managing multiple AWS application resources, then these tags can come quite useful.

For the platform, select "Python" from the dropdown, and it will fill the "platform branch" and "version" on its own.

For the application code, you will be uploading your code on elastic beanstalk later. So, for now, keep the "sample application" selected and hit the create application button. It should take a few minutes before it gets ready.

Creating a Flask app

While AWS is creating an environment for you, in the meantime, create a new file called

application.py
and put the following code in it:

from flask import Flask
import tweet_reply
import atexit
from apscheduler.schedulers.background import BackgroundScheduler

application = Flask(__name__)

@application.route("/")
def index():
    return "Follow @zeal_quote!"

def job():
    tweet_reply.respondToTweet('tweet_ID.txt')
    print("Success")

scheduler = BackgroundScheduler()
scheduler.add_job(func=job, trigger="interval", seconds=60)
scheduler.start()

atexit.register(lambda: scheduler.shutdown())


if __name__ == "__main__":
    application.run(port=5000, debug=True)

This is a simple flask app where you can create one function called

job()
, which will run every minute using
apscheduler
, which will eventually call the main function of your
tweet_reply.py
file.

Please note that Elastic Beanstalk expects the name of your flask application object instance to be

application
. If you will use any other name, then Elastic Beanstalk would fail to load your application.

Upload and configure the application to AWS

To configure the AWS resources and your environment, you can add Elastic Beanstalk configuration files (.ebextensions) to your web application's source code.

Configuration files are YAML formatted documents(JSON is also supported) with a .config file extension that is placed inside the folder named

.ebextensions
and deploy along with your application source code.

For this project, create a new folder

.ebextensions
in the source directory of your code and create a new file as
python.config
in that folder. Copy the code below code in it:

files:
  "/etc/httpd/conf.d/wsgi_custom.conf":
    mode: "000644"
    owner: root
    group: root
    content: WSGIApplicationGroup %{GLOBAL}

You will also need to create a

requirements.txt
file, which will contain all the external Python libraries that are required in your project for Elastic Beanstalk to configure the environment according to your application's need.

To create this file, simply run the following command:

bash
pip freeze > requirements.txt

Now you will need to zip all the files together to upload the flask app on Elastic Beanstalk. You should now have the following structure inside your project folder:

├── /.ebextensions
│   └── app.config
├── application.py
├── tweet_reply.py
├── Wallpaper.py
├── requirements.txt
├── Arial.ttf
└── tweet_ID.txt

Select all these files and folders mentioned and Zip all of them together. Go back to your AWS application & click on Upload Code. Deploy

Choose your zip file and click deploy. Then wait until your application gets deployed and the health symbol turns green. If you completed all these steps successfully, your website link should take you to a page saying, "Follow @zeal_quote!"

How to view error logs in your environment

For debugging your application in case any error comes, just follow the below steps to view the logs for your app:

  • In your Elastic Beanstalk dashboard, click on "Logs" from your environment section.
  • It will take you to another page where you will get a dropdown after clicking "Request Logs". Select the "Last 100 Lines" for the most recent errors, or you also have an option to download "full logs" in case you're debugging the error that occurred a long time back.
  • Click "Download" and it will take you to a page where you can view the last 100 lines of logs.

Logs.

in_logs

Wrapping Up

In this tutorial, you went through the complete process of developing and deploying a Twitter bot in Python.

You also learned about Tweepy, how to sign up as a Twitter developer to use its API, use Tweepy to invoke the Twitter API, and configure an AWS Elastic Beanstalk environment to deploy your Python application.

All the source code that has been used here is available in this GitHub Repository. To test the final working of the bot, you can look for @zeal_quote on Twitter.

Do check out the complete Tweepy API documentation to make more complex bots that are meaningful to your use case.