Get customized Tweet notifications where you want them
Tags: java, aws, serverless
Published: 2020-05-07 17:10:25


Many times people want to be notified when someone Tweets about a certain topic of interest to them, or when an account of interest to them Tweets something. For example, I might want to be notified whenever a person Tweets about Academic Research with Twitter data, so that I can learn about how that person is using Twitter APIs for their research and what kind of research they are doing. Similarly, somebody might want to be notified about breaking news stories or conversations pertaining to certain brands. They may want to be notified via email and sms or they may want to receive the links to the Tweets of interest as a message in their [Slack](https://slack.com/) or [Amazon Chime](https://aws.amazon.com/chime) channel. In this tutorial, you’ll learn how to build an app in Java that publishes links to Tweets based on user defined interests. Specifically, the app will publish Tweets from some news channel’s handles containing the words ”breaking news”. We’ll use Twitter’s [Recent Search API](https://developer.twitter.com/en/docs/labs/recent-search/overview) to push Tweets to tools such as Slack, Chime, or even as SMS, using [Twilio](https://www.twilio.com/). This is a serverless app and can be configured quickly to AWS Lambda without requiring code-changes.

_In order to start, you will need  a [Twitter developer account](https://developer.twitter.com/en/apply-for-access) and an [AWS account](https://aws.amazon.com/resources/create-account/). Slack, Chime and Twilio are optional (If these are not setup, then the app will simply print the links to the Tweets to your CloudWatch logs)._

As I mentioned above, this app will use v2 of Twitter’s [Recent Search API](https://developer.twitter.com/en/docs/labs/recent-search/overview). This is a preview of a future release of Twitter search endpoints and can be accessed via  [Twitter Developer Labs](https://developer.twitter.com/en/labs). It supports [15 operators](https://developer.twitter.com/en/docs/labs/recent-search/guides/search-queries) that support querying for Tweet attributes and can be 512 characters in length. Labs recent search requests support the Labs fields and expansions parameters, and return a [new Labs Tweet JSON design](https://developer.twitter.com/en/docs/labs/tweets-and-users/api-reference/get-tweets). 

Additionally, the app is ‘serverless’ and also demonstrates:
- AWS Lambda, Secrets Manager & CloudWatch
- Storing Tweet IDs in DynamoDB
- Publishing messages to Slack & Amazon Chime
- Sending SMS using Twilio using Java SDK

The complete source code is available on [GitHub](https://github.com/twitterdev/tweet-notifier).

#### Architecture
A cloudwatch event triggers an AWS Lambda function every hour. The Lambda function then calls the Twitter Recent Search API and gets the Tweets of interest for that time period. It stores the new Tweet IDs in DynamoDB and publishes the links to Tweets to Slack, Amazon Chime and sends it as SMS to the phone number configured with Twilio.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pyrglhkbixjyrn1n0bgd.png)

#### Step 1: Creating a Twitter App
In order to use the Twitter APIs in this app, you will need the following:
- A Twitter developer account. You can [apply for access](https://developer.twitter.com/en/apply-for-access) on Twitter Developer website.
- A Twitter [app](https://developer.twitter.com/en/apps).
- Your Twitter API keys. Our documentation explains [how to obtain them](https://developer.twitter.com/en/docs/basics/apps/overview).
- Enroll the app in [Twitter Developer Labs](https://developer.twitter.com/en/labs).

#### Step 2: Identifying Tweets of interest
You can search by Tweets from a particular account, Tweets that include images, media etc. and also filter on excluding certain Tweets or conditions. More information on building queries for recent search can be found on our [documentation page](https://developer.twitter.com/en/docs/labs/recent-search/guides/search-queries).

In this project, currently SEARCH_STRING is set to: 
"(from:@CNN OR from:@BBCWorld) lang:en \"breaking news\" -is:retweet" 
which means we are looking for all Tweets:
from the CNN or BBC World accounts
that are not a retweet and
are in English
contain the words ‘breaking news’

Additional details on implementing the recent search API in Java can be found in the [TwitterService.java]() file. Make sure to set the value of the SEARCH_STRING appropriately in your Lambda environment variable with your own search string for Tweets of interest to you. I use the ‘start_time’ query parameter to ensure that I am getting Tweets from 1 hour ago. The query parameters are added as shown below:

```java
URIBuilder uriBuilder = new URIBuilder("https://api.twitter.com/labs/2/tweets/search");
ArrayList<NameValuePair> queryParameters;
queryParameters = new ArrayList<>();
queryParameters.add(new BasicNameValuePair("query", searchString));
queryParameters.add(new BasicNameValuePair("start_time", startTime));
queryParameters.add(new BasicNameValuePair("expansions", "author_id"));
uriBuilder.addParameters(queryParameters);
```

#### Step 3: Setting up the Slack webhook
In order to send Tweets of interest to a Slack channel, you will need to create a webhook by following the [instructions on their documentation](https://api.slack.com/messaging/webhooks). Copy the webhook url as you will store this later in AWS Secrets Manager and use this to publish Tweets that match your criteria to your slack channel. Make sure you specify the name of your slack bot by setting it as an environment variable BOT_NAME in your Lambda function. In this sample app, I have used a third-party Java SDK for Slack that makes it easy for me to publish Tweets to a channel. More information on the implementation can be found in the [SlackService.java]() file.

#### Step 4: Setting up the Amazon Chime webhook
In order to send Tweets to a Chime channel, you will need to add a webhook to your chat room  – just [follow the Chime documentation](https://docs.aws.amazon.com/chime/latest/dg/webhooks.html) to set it up. Copy the webhook URL, so that you can  specify it  in AWS Secrets Manager later.  A POST call is required to the webhook url to publish messages to the chime channel as shown below:

```java
public void postMessage(String url) throws IOException {
 if(validParameters(webhook)) {
   HttpClient httpClient = HttpClients.custom()
       .setDefaultRequestConfig(RequestConfig.custom()
           .setCookieSpec(CookieSpecs.STANDARD).build())
       .build();

   HttpPost request = new HttpPost(webhook);
   StringEntity params =new StringEntity(String.format("{\"Content\":\"%s\"}", url));
   request.addHeader("content-type", "application/json");
   request.setEntity(params);
   httpClient.execute(request);
 }
}
```

More information on the implementation can be found in the [ChimeService.java]() file.

#### Step 5: Setting up Twilio to send and receive SMS
In order to send SMS using Twilio, you will need to create an account and set up a Twilio app. Please refer to the Twilio [documentation](https://www.twilio.com/docs/sms/quickstart/java) on getting started in Java. You will need the sid, token and the phone numbers you want to send and receive messages from. I have used Twilio’s Java SDK and the implementation can be found in the[ TwilioService.java]() file.

#### Step 6: Setting up a table in DynamoDB to store Tweet IDs
In this tutorial, we will store the Tweet IDs of the published Tweets in DynamoDB. In order to do that, you will have to create a table in DynamoDB with a primary key (String) to store the Tweet IDs. More information on creating tables in DynamoDB can be found [in their docs](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SampleData.CreateTables.html). 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/37d2i90xdpw05ef246hr.png)

Copy the table name and primary key id, because you will save them in AWS Secrets Manager. Implementation of storing the Tweet IDs in DynamoDB can be found in the [DynamoService.java]() file.

#### Step 7: Storing keys in AWS Secrets Manager
Instead of storing API Keys, Secrets, Webhooks etc. in properties files, we will store them in AWS Secrets Manager and use them in our code programmatically. AWS has a nice tutorial on [adding secrets to Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/tutorials_basic.html). 

First create a secret in your Secrets Manager console. 
Select Other type of secrets. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3cdb6ig9u7biyik2km64.png)

In the section where it says Specify the key/value pairs to be stored in this secret, add the following keys (and their corresponding values from the previous steps):
- twitter-api-key
- twitter-api-secret
- slack-webhook-url
- chime-webhook-url
- dynamo-table-name
- dynamo-table-pk
- twilio-sid
- twilio-token
- twilio-from-number
- twilio-to-number

Click next and give the secret a name and save it. Make sure to set the environment variable SECRET_NAME for your Lambda function with this secret name. Also make sure that the right AWS region environment variable REGION is set (by default it is set to us-east-1). Implementation of retrieving secrets from Secrets Manager can be found in the [SecretsManagerService.java]() file.

#### Step 8: Creating a CloudWatch Events rule
In order to invoke the Lambda function periodically, we will need to create a CloudWatch Trigger rule that will invoke the Lambda function (every hour in this example). If you need more details, you can follow a tutorial for [setting CloudWatch rule](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/RunLambdaSchedule.html) in the AWS docs. Make sure to set the trigger interval to 1 hour.

#### Step 9: Building the project
Ensure you have cloned the code from the [Github repository](https://github.com/twitterdev/tweet-notifier).

Using maven: go to the directory containing pom.xml, and run:

```mvn assembly:assembly -DdescriptorId=jar-with-dependencies package```

This will generate a zip file named "tweet-notifier-1.0-jar-with-dependencies.jar" in the target directory.

#### Step 10: Deploying to AWS Lambda
In order to deploy the code to Lambda:
1. Go to the [AWS Console](http://console.aws.amazon.com/) and create a Lambda function.
2. Select author from scratch and give your function a name (e.g. twitter-notifier) and select Java 8 as the runtime.
3. Select appropriate role and click create function.
4. For Code entry type, select "Upload a .ZIP file" and then upload the jar file created in the previous step (step 9) from the build directory to Lambda.
5. Set the Handler as LambdaHandler
6. Increase the Timeout to 30 seconds and make sure memory is set to at least 512 MB under Basic Settings.
7. Click add trigger, and select Cloudwatch Events/EventBridge. Under rules, click on the rule you created (in step 8) and click add.
8. Click manage environment variables and add the following environment variables
 - BOT_NAME (with the value of name for your Slack chatbot)
 - REGION (with the value of your AWS region)
 - SECRET_NAME (with the value of the name of your secret that has your keys)
 - SEARCH_STRING (with the search query string for the Tweets you wanted to be notified about)
9. Click save to complete setting up the Lambda function.

Once you have deployed the app to Lambda, you will receive links to your chosen Tweets of interest every hour delivered to your Slack or Chime channels or via SMS. 

I used several libraries and tools beyond the Twitter API to create this code sample, but you may have different needs and requirements and should evaluate whether those tools are right for you. Twitter does not operate or manage the third party tools mentioned above and those tools may have separate terms and limits regarding use of any webhooks and other features. Try it out and reach out to me on Twitter [@suhemparack](https://twitter.com/suhemparack) or [@TwitterDev](https://twitter.com/TwitterDev) with feedback or questions.