Simple Twitter Posting BOT

I decided to work on this, out of sheer laziness to keep posting regularly on Social Media (SM). If you are already an active member of SM Channels like Twitter, you will understand how important it is to post your tweets regularly.

Simple Twitter Posting BOT
Picture I took from Tirumala Hills

This is especially applicable to those who are photographers, graphic artists, etc. This may not be applicable to those commenting on current affairs and other such scenarios where it is mostly about responding to an existing tweet/post. This article uses AWS S3, AWS DynamoDB & AWS Lambda.

If you are a photographer or graphic artist, it is about tweeting/posting regularly to actively engage your existing followers and attract new ones.

This is what we are going to do

  • Create AWS S3 bucket and upload all the pictures
  • Create AWS DynamoDB table with the posting schedule
  • Create an app on the Twitter developer and get the keys
  • write code to pick the picture as per the schedule and post it on Twitter

S3 Bucket

Create AWS S3 bucket and upload all the images to the bucket and set permissions such that your Lambda code can access those images.

Create a Table in AWS DynamoDB

Create a DynamoDB table with columns as shown in the screenshot below. You are free to add more columns as necessary. I included a post-type column to differentiate posting images from pictures.

A simple table structure in AWS DynamoDB

Twitter Developer Part

You will need to create an application on the Twitter Developer portal. Make sure the app has full permissions to read, write and direct messages. Please verify this on your Twitter account settings. Look for connected apps and make sure it has all the permission you set on the developer portal.

Main Screen while creating Twitter Developer App
Make sure that you have full permissions on the App
Verify you have required permission on the twitter settings

...and the AWS Lambda NodeJS Code

As you can see in the code, we connect the DynamoDB table and pull the posts as per the date before posting them on Twitter based on if it is a picture or text.

'use strict';

const moment = require('moment'); 
const AWS = require('aws-sdk');
const mydocumentClient = new AWS.DynamoDB.DocumentClient();

const BUCKET_NAME = <s3 bucket name>;
const IAM_USER_KEY = <IAM User key>;
const IAM_USER_SECRET = <IAM User Secret>;

const s3bucket = new AWS.S3({
  accessKeyId: IAM_USER_KEY,
  secretAccessKey: IAM_USER_SECRET
});

const Twit = require('twit')

const T = new Twit({
  consumer_key:         <Twitter Consumer key>,
  consumer_secret:      <Twitter Consumer secret>,
  access_token:         <Twitter Access Token>,
  access_token_secret:  <Twitter Access Token secret>,
})

module.exports.run = async (event, context) => {
  const time = new Date();
  const params = {
    TableName : <DynamoDB Table name>,
    FilterExpression: 'postdate = :pdate',
    ExpressionAttributeValues: {":pdate": moment(time).format('MM/DD/YYYY')},
  };
  const data = await mydocumentClient.scan(params).promise();
  console.log('data', data.Items[0].post);
  if (data.Items[0].posttype=='quote') {
    const tweet = await postTweet(data.Items[0].post);
    console.log('tweet', tweet);
  } else {
    const tweet = await postpic(data.Items[0].post);
    console.log('tweet', tweet);
  }

  console.log(`Your cron function "${context.functionName}" ran at ${time}`);
};

const postTweet = (post) => {
  return new Promise ((resolve, reject) => {
    T.post('statuses/update', { status: post }, function(err, data, response) {
      if (err) {
        reject(err);
      }
      resolve(data);
    })
  })
}

const postpic = async (post) => {
  const data = await s3bucket.getObject({
    Bucket: BUCKET_NAME,
    Key: post
  }).promise()
  return new Promise ((resolve, reject) => {
    if (data != null) {
    T.post('media/upload', { media_data: Buffer.from(data.Body).toString('base64') }, function (err, data, response) {
      console.log('err', err);
      console.log('data1', data);
      var mediaIdStr = data.media_id_string
      var altText = "flowers" //if you are posting pictures of flowers
      var meta_params = { media_id: mediaIdStr, alt_text: { text: altText } }
     
      T.post('media/metadata/create', meta_params, function (err, data, response) {
        if (!err) {

          var params = { status: <Text with picture>, media_ids: [mediaIdStr] }
     
          T.post('statuses/update', params, function (err, data, response) {
            console.log(data)
            resolve(data)
          })
        }
      })
    })
  }

  })
}

Scheduling your Lambda

Finally, it is time to set your Lambda to run by itself at the scheduled hour. Read more about scheduling expressions in lambda here.