How to Make a Twitter Bot with Google Scripts

You can easily write a Twitter bot with the help of Google Apps Script. It fetches tweets addressed (@mentions) to the bot through the Twitter API, computes the answer with Wolfraph Alpha and replies with a tweet. The steps are:

  • Create a Twitter App at apps.twitter.com (screenshot) and note the Twitter Consumers Keys and Access Tokens
  • Go to developer.wolfram.com, create an account screenshot and then choose “Get an App ID” to create your new app. We will need this App ID in the Google Script. Your free Wolfram Alpha App can only be used for a non-commercial purpose.
  • Edit the values of TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET, TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_SECRET, TWITTER_HANDLE (your Twitter account) and WOLFRAM_API_ID
  • Go to Run -> Start to initialize the Twitter bot. Say yes if the script requires you to authorize access to certain Google services.

The code is ready to though you’ll need to create a Wolfram App and also include the Twitter library in your Google Scripts project (key is MKvHYYdYA4G5JJHj7hxIcoh8V4oX7X1M_) with the identifier set to Twitter.


/* Twitter Bot written by Amit Agarwal */

function Start() {

  var props = PropertiesService.getScriptProperties();

  props.setProperties({
    TWITTER_CONSUMER_KEY: TWITTER_CONSUMER_KEY,
    TWITTER_CONSUMER_SECRET: TWITTER_CONSUMER_SECRET,
    TWITTER_ACCESS_TOKEN: TWITTER_ACCESS_TOKEN,
    TWITTER_ACCESS_SECRET: TWITTER_ACCESS_SECRET,
    MAX_TWITTER_ID: 0
  });

  // Delete exiting triggers, if any

  var triggers = ScriptApp.getProjectTriggers();

  for (var i = 0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i]);
  }

  // Setup a time-based trigger for the Bot to fetch and process incoming Tweets 
  // every minute. If your Google Script is running out of quota, change the
  // time to 5 or 10 minutes though the bot won't offer real-time answers then.

  ScriptApp.newTrigger("labnol_twitterBot")
    .timeBased()
    .everyMinutes(1)
    .create();

}

/* For help, email amit@labnol.org or visit http://ctrlq.org */

function labnol_twitterBot() {

  try {

    var props = PropertiesService.getScriptProperties(),
      twit = new Twitter.OAuth(props);

    // Are the Twitter access tokens are valid?
    if (twit.hasAccess()) {

      var tweets = twit.fetchTweets("to:" + TWITTER_HANDLE,
        function(tweet) {
          // Ignore tweets that are sensitive (NSFW content)
          if (!tweet.possibly_sensitive) {
            var question = tweet.text.toLowerCase().replace("@" + TWITTER_HANDLE, "").trim(),
              answer = askWolframAlpha_(question);
            if (answer) {
              return {
                answer: "@" + tweet.user.screen_name + " " + answer,
                id_str: tweet.id_str
              };
            }
          }
        }, {
          multi: true,
          lang: "en",  // Fetch only English tweets
          count: 5,    // Process 5 tweets in a batch
          since_id: props.getProperty("MAX_TWITTER_ID")
        });

      if (tweets.length) {

       // The MAX_TWITTER_ID property store the ID of the last tweet answered by the bot
        props.setProperty("MAX_TWITTER_ID", tweets[0].id_str);

        // Process the tweets in FIFO order
        for (var i = tweets.length - 1; i >= 0; i--) {

          // The bot replies with an answer
          twit.sendTweet(tweets[i].answer, {
            in_reply_to_status_id: tweets[i].id_str
          });

          // Wait a second to avoid hitting the rate limits
          Utilities.sleep(1000);
        }
      }
    }

  } catch (f) {
    // You can also use MailApp to get email notifications of errors.
    Logger.log("Error: " + f.toString());
  }

}

function askWolframAlpha_(q, app) {

  try {

    var api = "http://api.wolframalpha.com/v2/query?podindex=2&format=plaintext&appid=" 
                   + WOLFRAM_APP_ID + "&input=" + encodeURIComponent(q);
    var response = UrlFetchApp.fetch(api, {
      muteHttpException: true
    });

    // Parse the XML response
    if (response.getResponseCode() == 200) {
      var document = XmlService.parse(response.getContentText());
      var root = document.getRootElement();
      if (root.getAttribute("success").getValue() === "true") {
        return root.getChild("pod").getChild("subpod").getChild("plaintext").getText();
      }
    }
  } catch (f) {}
  return false;
}