How to Have Alexa Read a Twitter Feed

TwitterDev profile image Suhem Parack

Building an Alexa Skill for Twitter using Alexa Presentation Language

In 2016, Twitter launched a skill for Alexa that allowed people to ask Alexa for their retweets, mentions etc. Back then, Alexa did not support display components. Since then, many Alexa devices now support display capabilities to provide a voice-first experience. This provides a rich, enhanced experience for people as they can view visual components such as images, text etc. on their devices, along with Alexa's voice-based experience.

This tutorial shows how to build an Alexa skill for Twitter that allows you to view Trends and Tweets on an Alexa device with display capabilities, such as the Echo Show. I will use the Alexa Presentation Language (APL) to build the display components. This sample is written in Java and uses the Alexa Skills Kit (ASK) SDK for Java. To obtain Tweets and Trends, I will use the Twitter4J library. The full working code with setup instructions can be found here.

Note: This tutorial assumes a basic understanding of building an Alexa Skill and APL. For a more introductory sample, refer to this training course from Amazon.

Setting up a Twitter App

In order to use the Twitter APIs in the Alexa skill, I needed the following:

  1. A Twitter developer account. Click here to apply
  2. A Twitter app
  3. My Twitter API keys. Please review the documentation about obtaining your keys here.

Once I retrieved the API keys, I could use them to create a Twitter Client in Twitter4J which allows me to retrieve Tweets and Trends from Twitter in my Alexa skill.

Building the Language Model for your Alexa Skill

The complete English language model JSON for this skill can be found here.

To support "what's trending in a city", I added a TrendIntent which has a slot of type City. A slot is the variable piece of information in an Intent. So, when people say something like "What's trending in Seattle", it will get routed to this TrendIntent. A complete list of slot types can be found here.

To support "give me Tweets about a topic", I added TweetIntent which has a custom slot type of SearchTerm. So, when a user says something like "Give me Tweets about snow", it will get routed to this TweetIntent.

Instructions on building this language model can be found here.

Getting Tweets and Trends

In this sample, I used the Twitter4J library to connect with the Twitter API in order to get Tweets and Trends. I created a TwitterService class that contains a function to get trends by location and another function that gets a List of Tweets for a search term. The Twitter client is setup as follows, using the Twitter API keys:

                          public              TwitterService              ()              {              twitter              =              new              TwitterFactory              (              new              ConfigurationBuilder              ()              .              setOAuthConsumerKey              (              API_KEY              )              .              setOAuthConsumerSecret              (              API_SECRET              )              .              setOAuthAccessToken              (              ACCESS_TOKEN              )              .              setOAuthAccessTokenSecret              (              ACCESS_TOKEN_SECRET              )              .              setTweetModeExtended              (              true              )              .              build              ())              .              getInstance              ();              }                      

Enter fullscreen mode Exit fullscreen mode

The getTrends() method in the TwitterService class takes in a location. We can then lookup the Where On Earth Identifier (WOEID) for that location using a local mapping that I maintain in the SkillData file. Using this WOEID, we can get a list of trends as shown below. Note: I am only using a small list of WOEID mapping for this sample. A more comprehensive list of available WOEIDs can be searched here.

                          public              Trends              getTrends              (              String              location              )              {              Trends              trends              =              null              ;              try              {              int              woeid              =              SkillData              .              LOCATION_MAP              .              get              (              location              );              trends              =              twitter              .              getPlaceTrends              (              woeid              );              }              catch              (              TwitterException              e              )              {              e              .              printStackTrace              ();              }              return              trends              ;              }                      

Enter fullscreen mode Exit fullscreen mode

The getTweetsBySearchTerm() takes in a search string. We build a search query using this term and obtain a list of Tweets as shown below. More details on building a search query can be found here.

                          public              List              <              Status              >              getTweetsBySearchTerm              (              String              searchTerm              )              {              Query              query              =              new              Query              (              String              .              format              (              "-filter:retweets -filter:images %s"              ,              searchTerm              ));              query              .              count              (              100              );              query              .              resultType              (              Query              .              POPULAR              );              QueryResult              result              =              null              ;              try              {              result              =              twitter              .              search              (              query              );              }              catch              (              TwitterException              e              )              {              e              .              printStackTrace              ();              }              return              result              .              getTweets              ();              }                      

Enter fullscreen mode Exit fullscreen mode

Building the Display Components for your Alexa Skill

In order to return a response with display components, I first needed to check if the device from which the request is coming supports APL or not. This function below demonstrates how that can be done:

                          public              static              boolean              supportsAPL              (              HandlerInput              input              )              {              SupportedInterfaces              supportedInterfaces              =              input              .              getRequestEnvelope              ().              getContext              ().              getSystem              ().              getDevice              ().              getSupportedInterfaces              ();              AlexaPresentationAplInterface              alexaPresentationAplInterface              =              supportedInterfaces              .              getAlexaPresentationAPL              ();              return              alexaPresentationAplInterface              !=              null              ;              }                      

Enter fullscreen mode Exit fullscreen mode

This skill supports three screens, optimized for Echo Show 5:

The welcome screen
welcome

The screen that provides the Trends as a list
trends

The screen that provides the Tweets
tweet

Currently, this sample supports three Tweets per search term. I use a Pager with SpeakItem command, so Alexa will read the Tweet on each page and then switch to the next one and read the next Tweet. Because I am using Java, I build a map for the datasource to be used in my response along with my APL document. A reference JSON equivalent for this datasource can be found here. The datasource contains the Tweet text, username, Twitter handle and the image URL of the user.

The TweetHandler handles the request for providing Tweets for a search term.

                          public              Optional              <              Response              >              handle              (              HandlerInput              handlerInput              ,              IntentRequest              intentRequest              )              {              final              RequestHelper              requestHelper              =              RequestHelper              .              forHandlerInput              (              handlerInput              );              final              String              searchTerm              =              requestHelper              .              getSlotValue              (              "SearchTerm"              ).              get              ();              final              List              <              Status              >              statuses              =              SkillData              .              getFilteredStatuses              (              twitterService              .              getTweetsBySearchTerm              (              searchTerm              ));              if              (              statuses              .              size              ()              <              3              )              {              return              getNotEnoughTweetsResponse              (              handlerInput              ,              searchTerm              );              }              final              String              tweetsAsSpeechText              =              SkillData              .              getTweetsAsSpeechText              (              statuses              );              final              String              speechText              =              String              .              format              (              "Here are some tweets about %s."              ,              searchTerm              );              if              (              SkillData              .              supportsAPL              (              handlerInput              ))              {              String              content              =              SkillData              .              getAplDocforTweets              ();              Map              <              String              ,              Object              >              document              =              new              Gson              ().              fromJson              (              content              ,              new              TypeToken              <              HashMap              <              String              ,              Object              >>()              {}.              getType              ());              Map              <              String              ,              Object              >              dataSource              =              new              Gson              ().              fromJson              (              SkillData              .              getDataSourceForTweets              (              statuses              ),              new              TypeToken              <              HashMap              <              String              ,              Object              >>()              {}.              getType              ());              List              <              Command              >              commandList              =              new              ArrayList              <>();              commandList              .              add              (              SpeakItemCommand              .              builder              ().              withComponentId              (              "tweet1"              ).              withDelay              (              500              ).              build              ());              commandList              .              add              (              SetPageCommand              .              builder              ().              withComponentId              (              "pagerId"              ).              withDelay              (              100              ).              withPosition              (              Position              .              RELATIVE              ).              build              ());              commandList              .              add              (              SpeakItemCommand              .              builder              ().              withComponentId              (              "tweet2"              ).              withDelay              (              500              ).              build              ());              commandList              .              add              (              SetPageCommand              .              builder              ().              withComponentId              (              "pagerId"              ).              withDelay              (              100              ).              withPosition              (              Position              .              RELATIVE              ).              build              ());              commandList              .              add              (              SpeakItemCommand              .              builder              ().              withComponentId              (              "tweet3"              ).              withDelay              (              500              ).              build              ());              SequentialCommand              command              =              SequentialCommand              .              builder              ()              .              withCommands              (              commandList              )              .              withDelay              (              300              )              .              build              ();              return              handlerInput              .              getResponseBuilder              ()              .              withSpeech              (              speechText              )              .              addDirective              (              RenderDocumentDirective              .              builder              ()              .              withDocument              (              document              )              .              withDatasources              (              dataSource              )              .              withToken              (              "pager"              )              .              build              ())              .              addDirective              (              ExecuteCommandsDirective              .              builder              ()              .              addCommandsItem              (              command              )              .              withToken              (              "pager"              )              .              build              ())              .              build              ();              }              return              handlerInput              .              getResponseBuilder              ()              .              withSpeech              (              String              .              format              (              "%s %s"              ,              speechText              ,              tweetsAsSpeechText              ))              .              build              ();              }                      

Enter fullscreen mode Exit fullscreen mode

Design Considerations

Since Alexa reads the Tweets that are shown on the screen of devices such as the Echo Show, one of the challenges was dealing with URLs and emojis in a Tweet. For this sample, on the screen we render the Tweets as-is, but strip the emojis etc. from the Speech Text that Alexa reads. Another thing to consider is maintaining the formatting of the Tweets to maintain consistency of how Tweets are displayed across platforms. You can read about the Twitter Display Requirements here.

Using APL, we are able to add visual components to build out the welcome, trends and Tweets screens to provide a rich, enhanced voice-first experience for Alexa users.

Instructions on deploying this code can be found here. Try it out and reach out to me on Twitter @suhemparack with feedback or questions.

pumphreydribithave.blogspot.com

Source: https://dev.to/twitterdev/building-an-alexa-skill-for-twitter-using-alexa-presentation-language-1aj0

0 Response to "How to Have Alexa Read a Twitter Feed"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel