How to build a Twitter weather bot with Node.js Part II: Responding to Tweets

Mehdi Chekori
7 min readNov 24, 2019

--

Hey everyone, welcome to part II of III of my how to build a Twitter Bot Series. If you haven’t read part I Click Here.

Today we are going to add a new feature where our bot will be able to read inputs from users and respond with the corresponding weather data.

As always you can find the source code in the Github repo.

Let’s get started!

This is how it will look like by the end of this tutorial :

As you can see I am Tweeting @ MtlWeatherBot followed by my city (Montreal), then I’m specifying the country code.

The bot then proceeds to respond with the current weather.

The Twit library offers the ability to listen to a stream. We can specify that we want to listen to the status stream and filter them by those who contain our username to get the tweets that are directed at us.

configs.username is our Twitter handle:’@mtlweatherbot’

Now that we are listening to the stream, every time that there is a tweet that contains @mtlweatherbot the TweetEvent Function will be called.

Let’s define that function.

The Tweet Event function will need to read that tweet, parse it to get the city & country code get their weather data and then reply.

The function will receive a tweet object that contains quite a lot of information. You can see an example here.

We will need :

  • the username of who tweeted at us (tweet.user.screen_name)
  • the reply identifier (tweet.id_str)
  • and the text (tweet.text)

The text will need to be parsed to make sure it follows our specified format.

Let’s create a new function called ParsedTweet(tweet.text) and we will pass it the text content of our tweet.

The reason we want the tweets to follow a specific pattern is to make it easier for us to find and reply with the right data. In the previous tutorial, we were accepting any tweet that had our username and a word that came after it. That can lead to errors from the API, or us returning the wrong data if for example the city that they are looking for exists in multiple states or countries.

An easy way to do so is to use a regex expression.

A regular expression, regex or regexp is a sequence of characters that define a search pattern. Usually such patterns are used by string searching algorithms for “find” or “find and replace” operations on strings, or for input validation. It is a technique developed in theoretical computer science and formal language theory.

We want all the tweets to follow this pattern @TheBotUsername ,The city, The country code.

At first glance, this looks complicated but we can break it down in sections.

@MtlWeatherBot : Matches “@Mtlweatherbot “ (with a space after the username)

@MtlWeatherBot ,Montreal,ca

(([a-z]*)|([a-z]* [a-z]*)) : Matches any 1 word composted with characters from a to z or any 2 words with a space in between

@MtlWeatherBot ,Montreal,ca

,[a-z]{2} : Matches any 2 character country code preceded by a comma

@MtlWeatherBot ,Montreal,ca

$ : Matches the end of a string

@MtlWeatherBot ,Montreal,ca

Finally, the second parameter (“i”) is to specify that we want to ignore the case.

If you would like to learn more while playing with regular expressions I recommend the website https://regexr.com/

To test if the tweet fits our regular expression we can use the test function.

If the tweet matches our pattern we want to extract the city and the country code from it.

To do so we will use the substring function and the split function.

The substring() method extracts the characters from a string, between two specified indices, and returns the new substring.

Since we only specified 15, that’s where the substring will start. 15 is the index where the city’s name starts.

This index depends on the length of your bot’s username.

The split() method is used to split a string into an array of substrings, and returns the new array.

The function also takes a separator parameter, in our case, it will be the comma.

Now that we can read a tweet, see if it matches the correct pattern and extract the city and the country code from it we want to throw an error if it doesn’t match our pattern.

The full function looks like this.

Back to the TweetEvent function!

So far the function looked like this.

Let’s call the ParseTweet function we created with the text from the tweet as a parameter.

Then we assign the results to two variables city and country. We then need to call the GetData function. But since in the first part of this tutorial we were always gathering the data for Montreal that function didn’t take a city and a country as a parameter.

We then need to refactor the GetData() to make it more flexible.

We start by making the function asynchronous and adding two parameters city and country.

and this function will return a new promise.

A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.

We don’t want this change to break the functions that were calling GetData from the previous tutorial, so we will add a condition that checks if we are specifying a city and a country and if we are we will change the URL we use to call the Open Weather Map API.

If the parameters are undefined we will be calling the default URL which is set to Montreal.

The request stays the same for one change which is that we need to call the resolve() function when we are done with our call. It will set the state of the promise to resolved.

Then in the else part of the code, we added a New Error. The error will be replied to the user later on.

Back to TweetEvent.

We want to call GetData() with the city and country extracted and once we get the result from the API, we want to construct our tweet.

You can easily do so by using the function ‘then’. It allows us to execute code once a promise is resolved.

Once we have the results from the API we then call CleanData() and we want to set the status for the reply.

If we pass a tweeter username as a parameter that means we are construction a tweet for a reply, if not it means we are constructing a tweet for a general broadcast.

Then we assign the status and the reply id to the params object to be used for the TwitterApi Post and you tweet your reply!

If anything went wrong during the process we want the bot to reply to let the user know.

In the catch, we reconstruct the params but this time we will use the content of the error as a status reply.

Finally, the full function should look like this.

Finally, let’s refactor the old Tweet() function to use the same logic.

We still want to broadcast Montreal’s weather once per hour as in the previous tutorial, so we call the function inside of setInterval().

🎉🥳 There you go! 🎉🥳

Our bot is now able to tweet Montreal’s weather every hour and reply to anyone that wants to know the weather for their own city!

You can see the results by Tweeting at @MtlWeatherBot

Don’t you wish you were at Cancun? Because I sure do!

Photo by Andreas NextVoyagePL on Unsplash

Thank you for reading this article. In the next tutorial, we will be hosting our project on the cloud and getting it to run automatically 24/7.

Stay tuned!

As always you can find the source code in the Github repo.

--

--