DEV Community

metacollective
metacollective

Posted on

Twitter BOT to send joke and image (lambda + typescript)

Twitter Bot

In this blog post, I am going to show you how you can build your own twitter bot which will periodically send out tweet (joke) with images

How will it work?

  • We will get a random joke from https://jokeapi.dev/ API. It's free with some limitation on hit rate etc.
  • We will get a random but relevant GIPHY image using https://developers.giphy.com/ API
  • We will create and send a tweet using twitter api and twitter-api-client node package

system design

End Result will look like this

Prerequisite

  • You should have a twitter account to register with twitter's developer portal
  • AWS access key & secret key to deploy on AWS

Step 1: Register with twitter's developer portal and get relevant keys

Getting keys from twitter's developer account is pretty easy and straight forward process. You can get a step by step guide from this video -

Step 2: Register with giphy's developer account and get their access key

Again, this is very easy and self explanatory (https://developers.giphy.com/), but in case you need some detailed information on this API and how to get its access keys, you can watch this video -

Step 3: Now you have your twitter & giphy keys, so let's get started with writing the bot code

We will be using the serverless framework to write this lambda function.

Install serverless globally

npm install -g serverless
Enter fullscreen mode Exit fullscreen mode

Create an typescript project from the serverless template library

sls create --template aws-nodejs-typescript
Enter fullscreen mode Exit fullscreen mode

Install required node packages for this project

npm install --save axios twitter-api-client
npm install --save-dev serverless-offline
Enter fullscreen mode Exit fullscreen mode

axios: Promise based HTTP client for the browser and node.js
twitter-api-client Node.js client for Twitter API
serverless-offline This Serverless plugin emulates AWS λ and API Gateway on your local machine to speed up your development cycles.

Define your function in serverless.ts like this

import type { AWS } from '@serverless/typescript';

const serverlessConfiguration: AWS = {
  service: 'twitter-bot',
  frameworkVersion: '2',
  custom: {
    webpack: {
      webpackConfig: './webpack.config.js',
      includeModules: true
    }
  },
  // Add the serverless-webpack plugin
  plugins: ['serverless-webpack', 'serverless-offline'],
  provider: {
    name: 'aws',
    runtime: 'nodejs14.x',
    apiGateway: {
      minimumCompressionSize: 1024,
    },
    environment: {
      AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
    },
  },
  functions: {
    twitterJokesBot: {
      handler: 'handler.twitterJokesBot',
      events: [
        {
          http: {
            method: 'get',
            path: 'twitterJokesBot',
          }
        }
      ]
    }
  }
}

module.exports = serverlessConfiguration;

Enter fullscreen mode Exit fullscreen mode

Here we have defined a function named twitterJokesBot and it will be of type GET

Make sure that you add your serverless-offline plugin to the list of plugins in serverless.ts

Define twitter-api-client

import { TwitterClient } from 'twitter-api-client';

export const twitterClient = new TwitterClient({
    apiKey: process.env.TWITTER_API_KEY,
    apiSecret: process.env.TWITTER_API_SECRET,
    accessToken: process.env.TWITTER_ACCESS_TOKEN,
    accessTokenSecret: process.env.TWITTER_ACCESS_SECRET,
});
Enter fullscreen mode Exit fullscreen mode

Make sure that you have added these keys and tokens into your environment

Define joke functions

import Axios, { AxiosResponse } from "axios";

/**
 * Get a joke from jokeapi.dev. If the length of tweet string is greater than 280 character then recurse till you find one.
 * @returns string
 */
export const getJokeTweet = async () => {
  //List of hashtags for the tweet
  const hashTags =
    "@metacollective9 #jokes #javascript #programming #dev #linux #java #programming #python #reactjs #DataScience #infosec #gamedev #BigData #serverless";

  const footerText = "From https://jokeapi.dev";

  //Get a random joke from jokeapi
  let joke: AxiosResponse = await Axios.get(
    "https://v2.jokeapi.dev/joke/Programming?blacklistFlags=nsfw,religious,political,racist,sexist,explicit&type=single"
  );

  // \n will add line breaks to the tweet
  const tweet = `${joke.data.joke} \n\n ${hashTags} \n\n ${footerText}`;

  console.log(tweet.length);

  if (tweet.length <= 280) {
    return tweet;
  }

   return await getJokeTweet();

};

/**
 * Get a gif image from giphy.com
 * @returns image buffer
 */
export const getJokeImage = async () => {
  //get a giphy to go with this joke
  let giphy: AxiosResponse = await Axios.get(
    `https://api.giphy.com/v1/gifs/random?api_key=${process.env.GIPHY_API_KEY}&tag=bad+joke&rating=g`
  );

  //Get that giphy image as a buffer.
  const image: AxiosResponse = await Axios.get(giphy?.data.data.images.downsized_medium.url, {
    responseType: "arraybuffer",
  });

  return image;
};

Enter fullscreen mode Exit fullscreen mode

There are two parts of this

  • getJokeTweet is to get a random joke from jokeapi.dev, then adds your hashtags and other texts that you want to attach with this tweet. It also checks that it is not more than 280 characters (tweet character limit as of Feb 2022), if it is then recurse and gets another joke Lookout for blacklistFlags in the API call. This makes sure that you get a nice & clean joke
  • getJokeImageis to get a random giphy image using the giphy api keys we got earlier. You have to get it as an arraybuffer so that we can upload it to twitter as a base64 image

Put them all together in your handler.ts

import { APIGatewayProxyHandler } from "aws-lambda";
import "source-map-support/register";
import { twitterClient } from "./src/util/twitterApiClient";
import { getJokeImage, getJokeTweet } from "./src/util/joke";
import { AxiosResponse } from "axios";
import { MediaUpload } from "twitter-api-client";
export const twitterJokesBot: APIGatewayProxyHandler = async (_event, _context) => {
  let tweet: string = "";

  try {
    //get Joke
    tweet = await getJokeTweet();

    const image: AxiosResponse = await getJokeImage();

    //Upload media to twitter
    const media: MediaUpload = await twitterClient.media.mediaUpload({
      media: Buffer.from(image.data, "binary").toString("base64"),
    });

    //Send a tweet with joke and media
    await twitterClient.tweets.statusesUpdate({
      status: tweet,
      media_ids: media.media_id_string, 
    });
  } catch (error) {
    console.log(error);
  }

  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        joke: tweet,
        length: tweet.length
      },
      null,
      2
    ),
  };
};
Enter fullscreen mode Exit fullscreen mode

This is also a 2 step process

  • First is to upload a media asset to your twitter account
  • Then use the media_id_string from the response of uploaded media and send the tweet with it

That's it, now you are all set to test it -

test giphy

Run sls offline --stage local in your terminal and you should have your endpoint ready to test with like this

sls offline

🎉🎉🎉 Your bot is ready to launch.

If you wish to deploy it to your AWS account, then set your AWS Access Key & AWS secret Key in the environment and run sls deploy --stage dev

You can get the entire example from this repository https://github.com/appletreeat56/twitter-bot

Top comments (0)