Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough

Integrating Amazon Alexa With a Drupal 8 Site

Parent Feed: 

If you’ve ever used Alexa, it may seem like it must be extremely complicated to get her to respond like she does. However, if you have your content inside Drupal, it’s not terribly difficult to get her to utilize that data for your own custom Alexa skill. Let’s take a look at how to accomplish that.
 

Add the Alexa module to your website 

To have your website talk to Amazon, it has to be seen by Amazon’s servers. With that said, your site will have to be outward facing with an https domain. This isn’t an endorsement for them but for this example, we’ll be using a Pantheon hosted site, which allows for free Drupal dev sites to be quickly spun up. To implement this, the first thing we will need to do is add the Alexa module to our Drupal 8 site.
 

Set up an Alexa Skill on the Amazon Developer site 

After your site is up and you have the module installed, it’s time to start setting up the Amazon side of things. You’ll first need to create a developer account at https://developer.amazon.com. Next, you’ll need to set up a new Alexa Skill.

Once you’ve created a new Alexa skill, it’s time to start configuring it to be able to reach our site. The first thing to note is the Application ID that Amazon provides to you. That will need to be copied into the Alexa modules config located on your site at /admin/config/services/alexa

Next, back on the Amazon Development site, give your app a Name and an Invocation Name. The Invocation Name is what the end user will have to utter in order to activate the skill. For our example, we’ll be using the Invocation Name of ‘world publish’.

Next, click on Interaction Model. This is where we will begin to setup our custom commands that Alexa can respond to. These commands are called intents. Here we have setup 2 custom intents, readArticle and WorldPublish.

The other four listed are the default intents that Amazon provides to all Alexa apps and won’t require any setup on this side from us (though we will set a custom value for the AMAZON.HelpIntent when we are writing our custom module).

Our WorldPublish intent here will be used to ask Alexa what the five latest articles are on our site.

To have a better chance the user utters the right intent,  you can add more phrasings of this same question to make sure Alexa still will respond appropriately. In our custom module, we will set up the actions required for Alexa to respond back to this question by reading out the titles of the latest articles by date.

Once Alexa has given the user the names of the newest articles, the user may want one of those articles read to them. In our readArticle intent, we create two ways for the user to get an article read back to them. This intent also shows how we can use variables in our command. This variable will be sent to our custom module where we can use that to pull in the name of the article we want Alexa to read back to us.
 

Create custom module for your responses 

Now that we have some intents for Alexa to use, it’s time to start writing some code for what happens when a command is sent to your website. We’ll be naming the module demo_alexa.

In the demo_alexa.info.yml file put in the following:

name: Alexa Latest Articles Demo
type: module
description: Demonstrates an integration to Amazon Echo.
core: 8.x
package: Alexa
dependencies:
 - alexa

Be sure to add the Alexa module as a dependency. Next, we need to create the file that will do all the heavy lifting in our custom module. Create a file inside src/EventSubscriber/ and call it RequestSubscriber.php

Let’s break down what code goes into this file. First, we need to create a namespace and our use statements.

namespace Drupal\demo_alexa\EventSubscriber;

use Drupal\alexa\AlexaEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\paragraphs\Entity\Paragraph;

Next, we need to make our main class and a function that gets the event.

/**
* An event subscriber for Alexa request events.
*/
class RequestSubscriber implements EventSubscriberInterface {

 /**
  * Gets the event.
  */
 public static function getSubscribedEvents() {
   $events['alexaevent.request'][] = ['onRequest', 0];
   return $events;
 }

Now we can make the function that gives responses for each of our intents. Here’s the code for that:

/**
* Called upon a request event.
*
* @param \Drupal\alexa\AlexaEvent $event
*   The event object.
*/
public function onRequest(AlexaEvent $event) {
 $request = $event->getRequest();
 $response = $event->getResponse();

 switch ($request->intentName) {
   case 'AMAZON.HelpIntent':
     $response->respond('You can ask "what are the latest articles" and I will read the titles to you');
     break;

   case 'WorldPublish':
     $latestArticles = current_posts_contents();
     $response->respond($latestArticles);
     break;

   case 'readArticle':
     $article = $request->getSlot('Article');
     $articleResponse = current_post_body($article);
     $response->respond($articleResponse);
     break;

   default:
     $response->respond('Hello World Publish User. I can tell you the latest articles.');
     break;
 }
}

Here you can see the name of our two custom intents that we created earlier, WorldPublish and readArticle. You can also see how this can be used to set values for the default intents such as AMAZON.HelpIntent where when the user asks Alexa for help, we now have a custom response that Alexa will respond back to the user with. This function also gives us the ability to set a default response for if Alexa doesn’t understand what the question was. This is a good place to give the user the phrasing needed to get the proper response from Alexa.

Let’s take a look in more detail at the two custom intents we have in the code above. First off we have the WorldPublish intent.

   case 'WorldPublish':
     $latestArticles = current_posts_contents();
     $response->respond($latestArticles);
     Break;

If the WorldPublish intent is the one the user uttered, then we’ll run a custom function named current_posts_contents and then Alexa will respond back with the response from that function. Here is what that custom current_posts_contents function is doing:

/**
* Get latest articles function.
*
* Set beginning and end dates, retrieve posts from database
* saved in that time period.
*
* @return array
*   A result set of the targeted posts.
*/
function current_posts_contents() {
 $query = \Drupal::entityQuery('node');
 // Return the newest 5 articles.
 $newest_articles = $query->condition('type', 'article')
   ->condition('status', 1)
   ->sort('created', 'DESC')
   ->pager(5);

 $nids = $query->execute();

 $fullstring = 'Here are the five latest articles from WorldPublish, ';

 foreach ($nids as $nid) {
   $node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
   $name = $node->getTitle();
   $fullstring = $fullstring . ' ' . $name . ',';
 }

 $fullstring = $fullstring . '. You can have me <phoneme alphabet="ipa" ph="ɹˈiːd">read</phoneme> you one of the articles by saying, Alexa read, and then the title of the article.';

 return($fullstring);
}

This function does a query of the content type named ‘article’, with the condition that the status is set to publish, ordered by when the article was created and set to return the first five results. This query will give us an array of nid’s that we can use in a foreach loop . We then load the title of the nid and append that title to a string that will be Alexa’s response.

Another thing to note in this code is that sometimes, Alexa won’t get the phonetic pronunciation of words correct. In the statement “You can have me read you one of the articles”, Alexa was saying “red” instead of “reed” when pronouncing “read”. To correct that we can use the Speech Synthesis Markup Language to directly tell Alexa how to pronounce a specific word. To get the correct sounds, you can use the phoneme alphabet that is provided on Amazon’s help site.

<phoneme alphabet="ipa" ph="ɹˈiːd">read</phoneme>

By using the phoneme alphabet in this code, Alexa will change her pronunciation to the correct way to say the word “read” in this sentence.

Now that we have had Alexa read us a list of the five latest articles, let’s take a look at the readArticle case:

  case 'readArticle':
     $article = $request->getSlot('Article');
     $articleResponse = current_post_body($article);
     $response->respond($articleResponse);
     Break;

So here we can see where our ‘Article’ variable we set in our intent on the Amazon Developer site comes into play. We’re first grabbing that value from what the user tells Alexa and storing it inside of $article. Then we’re sending that value to a custom function called current_post_body to get our article for Alexa to read. Here’s what’s inside that function:

/**
* Get body of an article.
*
* Get body of article based on title.
*
* @return array
*   A result set of the targeted posts.
*/
function current_post_body(&$article) {

 $query = \Drupal::entityQuery('node');
 // Return the newest 5 articles.
 $article_query = $query->condition('type', 'article')
   ->condition('status', 1)
   ->condition('title', $article, 'CONTAINS')
   ->sort('created', 'DESC')
   ->pager(1);

 $nids = $query->execute();

 if ($nids) {
   foreach ($nids as $nid) {
     $node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
     $name = $node->getTitle();
     $foo = '';
     foreach ($node->field_paragraph as $item) {
       $target_id = $item->target_id;
       $paragraph_node = Paragraph::load($target_id);
       $addition = $paragraph_node->field_wysiwyg->value;
       // Remove html tags from value.
       $addition = strip_tags($addition);
       $foo = $foo . $addition;
     }
   }
   $response = $response . 'Here is the article, ' . $name . '. ' . $foo;
 }
 else {
   $response = 'I do not see an article by that name. To have me list the latest 5 articles, say, alexa what are the latest articles.';
 }

 return($response);
}

First thing this code does is it runs a query looking through the published article nodes looking for a title that contains the string that the user input. It will then return the latest article it can find that contains that string. From there if we found a piece of content that matches up, we then grab all the values from the paragraph fields inside our article. Just a note that if you aren’t using paragraph fields for your content then this is where you could return whichever fields you wanted Alexa to read back. Lastly, our code then strips out any html tags from the created string so all that is left is the text of the article. This string is now ready to be given back to Alexa for her response.

Now that we have our code for our responses back to Alexa, the last file we’ll need is demo_alexa.services.yml

services:
 alexa_demo.request_subscriber:
   class: Drupal\demo_alexa\EventSubscriber\RequestSubscriber
   tags:
     - { name: event_subscriber }

This should be all the code you’ll need to now test out your Alexa skill. Be sure to enable your demo Alexa module and then head back over to the Amazon Developer site.
 

Test the responses from the Amazon Developer Site 

Back at developer.amazon.com click on the Test tab and make sure your skill is enabled for testing on your account.

As of the time of this blog posting, there’s currently an issue with the test text simulator not working correctly but there’s an easy workaround for that. Be sure on “Use Service Simulator to test your HTTPS endpoint” that your web domain is listed in the box. Then you should be ready to enter in an Utterance into the text box. Type “help” into the box and this will send that to our code we just wrote. When you click the submit button it should send the AMAZON.HelpIntent request to our code and our code will respond back with the custom response we gave.

As you can see though, it looks like it didn’t work. This is the issue that Amazon is aware of and is currently fixing. We can get by this though by copying everything inside the Service Request box and then pasting it into the Json Request box in the JSON tab.

You can then click on ‘Listen’ and you’ll hear Alexa repeat back what the website sent as the response! You can try it again by asking Alexa ‘What are the latest articles’ and if you have articles on your site it should list out the articles for you.

Here’s a quick video showing this custom module working using an Amazon Echo Dot. This shows off how to activate the demo on the Echo, the commands for listing the articles, reading the article specified and also one of the built in default commands to stop Alexa talking. Be warned that if you have an Echo in your room with you, it will also try to respond to the commands said in this video as well!

So that’s it! With this code, you have a good example of how easy it can be to get Alexa to talk to your website. This could easily be used to create lots of different, fun interactive ways for your users to access all of the various content on your Drupal website.

Additional Resources
Building Rest Endpoints with Drupal 8 | Blog
Creating Content with YAML Content Module | Blog
Mediacurrent's Drupal Theme Generator | Blog

Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web