How to Effectively Manage Your Client-Partner Relationships
Clients Project StrategyClients leave when they feel that their needs aren’t being met. The strength of your client-partner relationship relies on a deep understanding of your client’s...
Editor’s note: This is the sixth post in our series on developing Alexa skills.
One of the greatest features of Alexa is that it functions as a personal assistant you can interact with without having to physically touch the device. This allows you to get information or accomplish tasks while you are, for example, baking a cake. One of the tasks you could accomplish in such a sticky situation could be to post a tweet about your baking adventures.
From an Alexa developer’s point of view, the task of posting a tweet is a pretty sophisticated operation because the skill needs to authenticate with the user’s Twitter account on the web, then get authorization to access the API in order to make a posting.
From a convenience and security point of view, it would be a terrible idea for the skill to ask for the user’s credentials verbally every time access to the Twitter API is needed. Furthermore, an Alexa-enabled device does not have a way to store these credentials locally, so another approach must be used.
Fortunately, the Alexa Skills Kit features account linking, which lets you access user accounts on other services, Twitter among them, using the OAuth protocol. In this post, we will use account linking and OAuth to grant delegated authority to our Airport Info skill so that it can post an airport’s flight status to a user’s Twitter account. Delegated authority means that the Airport Info skill will be granted permission to post to the user’s Twitter account without ever having access to the actual account credentials.
Note that Alexa uses the OAuth 2.0 protocol, and some services like Twitter still use version 1.0. The differences in the implementation are not great. Essentially, dealing with OAuth 1.0 requires an additional token request step that will be handled in this exercise by a separate web application.
If you haven’t already built an Alexa Skill, check out our previous posts on building Airport Info to get started.
The first step in enabling Twitter delegated authority to the Airport Info skill is to let Twitter know that the skill exists. We must register Airport Info as a Twitter App, so that Twitter knows the skill will later ask for authorization to post on a user’s behalf. To accomplish this, first log in to your Twitter account and visit the Twitter Apps page.
Now click on the “Create New App” button to see the application details page. In the “Name” field, enter a descriptive name for the skill. Note that this name needs to be unique across the entire Twitter Apps namespace, for all developers. Therefore, you may need to choose a name other than what is seen in the figure below. Any name collisions will result in the following error message: “The client application failed validation: Name has already been taken.”
In the “Description” field, enter a short string of text describing your Twitter app. It will be displayed when a user grants the skill the authority to post on Twitter.
In the “Website” field, enter a URL that users can visit to get more information about your skill and how it uses Twitter. In this case, we will enter “https://alexa-twitter-airport-info.herokuapp.com/app”.
This link corresponds to a Heroku-hosted web app we created for this post. It serves as a simple OAuth client or intermediary you can use for this experiment without having to host your own application, which could be an AWS Lambda function just like your skill. Note that this application has a very minimal web interface. You can also get the code for this app from GitHub.
Here’s what the “Create an application” page should look like at this point:
The final field is a “Callback URL”. This is the URL that will be loaded after a new user grants delegated authority to Airport Info by authenticating with Twitter. Once again, you will use the alexa-twitter-airport-info OAuth web app described above for this purpose. (The details of what happens when this URL is called back will be explained a little later on in this post.)
Finish the registration by accepting the developer agreement and clicking on the “Create your Twitter application” button. You will be redirected to the details page for your new application. Click on the “Keys and Access Tokens” tab to view the Consumer Key and Consumer Secret for your application.
Copy the Consumer Key (API Key) and Consumer Secret (API Secret) as you will be needing them soon.
The Consumer Key and the Consumer Secret will be used to authenticate the Airport Info skill when it needs to request authorization to tweet on the user’s behalf.
Finally, click on the permissions tab and set the “Access” type to “Read, Write and Access direct messages”. Click on the “Update Settings” button to save your change.
Now that Twitter has been informed that Airport Info may ask for API access, you need to configure the skill to use that privilege. Log in to the Alexa Skills Developer Portal and display your skills list. Assuming that you have already built and staged the Airport Info skill, click on its name on the list to display its settings, and then advance to the “Configuration” stage. Enable account linking by clicking on the “Yes” radio button. After you make that selection, additional fields will appear.
“Authorization URL” is one of the new fields activated by enabling account linking. This is a crucial bit of information because it is the URL a user will be directed to in order to grant delegated authority to the skill. In this particular case, redirecting to this URL should result in the Twitter log-in being displayed to the user. As the user authenticates with Twitter, the OAuth workflow is triggered, and an access token is created for the user and stored with Amazon.
Once again, you will use the alexa-twitter-airport-info OAuth web app as an intermediary to manage the OAuth workflow. This OAuth web application is designed in such a way that you and any other Alexa developer trying this exercise can use it instead of deploying their own OAuth app. The compromise in achieving this multi user flexibility is that you will have to pass developer-specific information such as the Twitter App Consumer Key and Consumer Secret when you call the OAuth web app URL. Even though the URL with the Twitter App information will be called with HTTPS, you would not want to do this in a production implementation. In that case, you would have to keep the Consumer Key, Consumer Secret and any other OAuth information securely stored in the hosting server.
The “Authorization URL” field contains the URL that has to be called on the alexa-twitter-airport-info OAuth web app with the Twitter App key, secret and vendor id.
In the “Privacy Policy URL” field, you should indicate a web page that would describe your skill’s privacy policy. Enter “https://alexa-twitter-airport-info.herokuapp.com/policy” for this test scenario. Click on the “Save” button to finish configuring the skill.
As you have finished configuring the Airport Info skill for account linking, you are now ready to walk through every step of the process.
As a new Airport Info user enables the skill for an Alexa device, the Alexa App will redirect them to the Authorization URL you specified in the previous section. This URL corresponds to the /oauth/request_token
endpoint of the alexa-twitter-airport-info application. Calling this endpoint with the appropriate information (Consumer Key, Consumer Secret, Vendor ID) generates a request for Twitter to return an access token. This request results in a redirection to a Twitter authentication page that asks a user to enter a username and password.
As the authentication is completed, Twitter generates an access token and redirects to the callback URL we specified when configuring the Twitter App. This URL corresponds to the /oauth/callback
endpoint of the alexa-twitter-airport-info OAuth web app. This endpoint redirects to the “Redirect URL” specified in the skill’s account linking page with the Twitter access token just generated. This token does not expire and is saved for the particular skill user with Amazon. The token is now available within the Airport Info code as you will see shortly.
The entire account linking process is described in the diagram below.
As mentioned earlier, the Twitter access token is available within the skill’s code, specifically from the sessionDetails
object available from the request
passed to a handler function. This will allow you to implement a new intent handler to post information to Twitter.
Open index.js
in your Airport Info skill local development project and add the following new handler (here’s more info on implementing the skill):
app.intent('tweetAirportStatusIntent', {
'slots': {
'AIRPORTCODE': 'FAACODES'
},
// Add ‘tweet’ to utterances:
'utterances': ['tweet {|delay|status} {|info} {|for} {-|AIRPORTCODE}']
},
function(request, response) {
var accessToken = request.sessionDetails.accessToken;
if (accessToken === null) {
//no token! display card and let user know they need to sign in
} else {
//has a token, post the tweet!
}
});
Notice how the availability of request.sessionDetails.accessToken
is tested. If it is not available, the user must be informed that they must link accounts. Update the code as shown below:
function(request, response) {
var accessToken = request.sessionDetails.accessToken;
if (accessToken === null) {
response.linkAccount().shouldEndSession(true).say('Your Twitter account is not linked.
Please use the Alexa App to link the account.');
return true;
} else {
//has a token, post the tweet!
}
});
The response.linkAccount()
method displays an appropriate card on the Alexa App to guide the user on how to link accounts. This is the same card displayed when you enable the skill for the first time.
The logic for posting airport status information will be handled by a new helper object called TwitterHelper
. In turn, this helper object will rely on the Twit Node.js package. To install Twit, open a terminal console and navigate to the airportinfo
folder within your alexa-app-server
directory used for local development. Once in the airportinfo
directory, issue the following command to install Twit:
$ npm install twit --save
Now create a new file in the airportinfo
directory called twitter_helper.js
and type the code below.
'use strict';
module.change_code = 1;
var _ = require('lodash');
var Twitter = require('twit');
var CONSUMER_KEY = 'XXXXX';
var CONSUMER_SECRET = 'XXXXX';
function TwitterHelper(accessToken) {
this.accessToken = accessToken.split(',');
this.client = new Twitter({
consumer_key: CONSUMER_KEY,
consumer_secret: CONSUMER_SECRET,
access_token: this.accessToken[0],
access_token_secret: this.accessToken[1]
});
}
TwitterHelper.prototype.postTweet = function(message) {
return this.client.post('statuses/update', {
status: message
}).catch(function(err) {
console.log('caught error', err.stack);
});
};
module.exports = TwitterHelper;
Be sure to replace the “XXXXX” placeholders with your Twitter App Consumer Key and Consumer Secret.
The TwitterHelper()
function accepts the access token and creates an instance of the TwitterHelper
object with authorization to post. The postTweet()
method can then be used to post text to the user’s Twitter account.
Open index.js
and add the following line of code right below any other require()
call.
var TwitterHelper = require(‘./twitter_helper’);
Now you can complete the implementation of the tweetAirportStatusIntent
with the code below. Please note the whole intent is listed and the new code is only within the else
clause as indicated.
app.intent('tweetAirportStatusIntent', {
'slots': {
'AIRPORTCODE': 'FAACODES'
},
'utterances': ['tweet {|delay|status} {|info} {|for} {-|AIRPORTCODE}']
},
function(request, response) {
var accessToken = request.sessionDetails.accessToken;
if (accessToken === null) {
response.linkAccount().shouldEndSession(true).say('Your Twitter account is not linked.
Please use the Alexa app to link the account.');
return true;
} else {
// New code begins here:
// I've got a token! make the tweet.
var twitterHelper = new TwitterHelper(request.sessionDetails.accessToken);
var faaHelper = new FAADataHelper();
var airportCode = request.slot('AIRPORTCODE');
if (_.isEmpty(airportCode)) {
var prompt = 'i didn't have data for an airport code of ' + airportcode;
response.say(prompt).send();
} else {
faaHelper.getAirportStatus(airportCode).then(function(airportStatus) {
return faaHelper.formatAirportStatus(airportStatus);
}).then(function(status) {
return twitterHelper.postTweet(status);
}).then(
function(result) {
response.say('I've posted the status to your timeline').send();
}
);
return false;
}
// New code ends here.
}
});
Now you need to replace the code in your AWS Lambda function with the code for the Tweeter-enabled version of the Airport Info skill.
Create a new zip archive with all the contents of the airportinfo
folder, and then go to the AWS Lambda console. Click on the Airport Info function and then click on the “Upload” button. Select your archive and update the skill.
You have just updated the skill code, but you still need to update the skill’s interaction model because you added a new intent and new utterances. Go back to your skills list in the Amazon Developer Console and click on the Airport Info skill. Then advance to the “Skill Interaction” page.
Copy the schema below and paste it to the “Intent Schema” field on the “Interaction Model” page.
{
"intents": [
{
"intent": "tweetAirportStatusIntent",
"slots": [
{
"name": "AIRPORTCODE",
"type": "FAACODES"
}
]
},
{
"intent": "airportInfoIntent",
"slots": [
{
"name": "AIRPORTCODE",
"type": "FAACODES"
}
]
}
]
}
Next, copy the updated utterances list including the tweetAirportStatusIntent
and paste it to the “Utterances” field on the “Interaction Model” page.
tweetAirportStatusIntent tweet {AIRPORTCODE}
tweetAirportStatusIntent tweet delay {AIRPORTCODE}
tweetAirportStatusIntent tweet status {AIRPORTCODE}
tweetAirportStatusIntent tweet info {AIRPORTCODE}
tweetAirportStatusIntent tweet delay info {AIRPORTCODE}
tweetAirportStatusIntent tweet status info {AIRPORTCODE}
tweetAirportStatusIntent tweet for {AIRPORTCODE}
tweetAirportStatusIntent tweet delay for {AIRPORTCODE}
tweetAirportStatusIntent tweet status for {AIRPORTCODE}
tweetAirportStatusIntent tweet info for {AIRPORTCODE}
tweetAirportStatusIntent tweet delay info for {AIRPORTCODE}
tweetAirportStatusIntent tweet status info for {AIRPORTCODE}
airportInfoIntent {AIRPORTCODE}
airportInfoIntent flight {AIRPORTCODE}
airportInfoIntent airport {AIRPORTCODE}
airportInfoIntent delay {AIRPORTCODE}
airportInfoIntent flight delay {AIRPORTCODE}
airportInfoIntent airport delay {AIRPORTCODE}
airportInfoIntent status {AIRPORTCODE}
airportInfoIntent flight status {AIRPORTCODE}
airportInfoIntent airport status {AIRPORTCODE}
airportInfoIntent info {AIRPORTCODE}
airportInfoIntent flight info {AIRPORTCODE}
airportInfoIntent airport info {AIRPORTCODE}
airportInfoIntent delay info {AIRPORTCODE}
airportInfoIntent flight delay info {AIRPORTCODE}
airportInfoIntent airport delay info {AIRPORTCODE}
airportInfoIntent status info {AIRPORTCODE}
airportInfoIntent flight status info {AIRPORTCODE}
airportInfoIntent airport status info {AIRPORTCODE}
airportInfoIntent for {AIRPORTCODE}
airportInfoIntent flight for {AIRPORTCODE}
airportInfoIntent airport for {AIRPORTCODE}
airportInfoIntent delay for {AIRPORTCODE}
airportInfoIntent flight delay for {AIRPORTCODE}
airportInfoIntent airport delay for {AIRPORTCODE}
airportInfoIntent status for {AIRPORTCODE}
airportInfoIntent flight status for {AIRPORTCODE}
airportInfoIntent airport status for {AIRPORTCODE}
airportInfoIntent info for {AIRPORTCODE}
airportInfoIntent flight info for {AIRPORTCODE}
airportInfoIntent airport info for {AIRPORTCODE}
airportInfoIntent delay info for {AIRPORTCODE}
airportInfoIntent flight delay info for {AIRPORTCODE}
airportInfoIntent airport delay info for {AIRPORTCODE}
airportInfoIntent status info for {AIRPORTCODE}
airportInfoIntent flight status info for {AIRPORTCODE}
airportInfoIntent airport status info for {AIRPORTCODE}
Click on the “Save” button to finish updating the skill configuration.
To test the account linking experience for a new Airport Info user, go to the Alexa app and search for Airport Info (or whatever your skill is named). The skill should be currently enabled, so you must temporarily disable it by clicking “Disable” and then the “Disable Skill” button. If you are unable to find your skill, make sure that you have enable testing for it, in the “Test” panel of the Skill’s Configuration Page.
If you now click on “Enable”, you should be redirected to the Twitter login page. After authenticating, you will be asked if you want to authorize Airport Info to use your Twitter account. Click on the “Authorize app” button.
After authorizing the app, you will be redirected to the skill page and you should see a success message confirming the account linking.
In the event that you did not receive a successful link card, there are a number of things you can validate to determine the root cause of the issue. First, make sure all information in your skill’s Account Linking configuration page is properly set, and that the Authorization URL is properly encoded. Next, make sure your CONSUMER_KEY and CONSUMER_SECRET values are accurately pasted in the Twitter Helper Object you created earlier. Lastly, you can check your CloudWatch Logs for any recent errors reported from your AWS Lambda service. Check out the Amazon docs on accessing CloudWatch logs if you need more info.
At this point, you should have your updated skill available on your Alexa-enabled device. Ask Alexa to tweet the status for ATL by saying, “Alexa, ask Airport Info to tweet flight status for ATL”.
If everything is correctly linked, you should see a tweet on your Twitter account.
In this series on building an Alexa skill, we’ve covered a lot of ground, from setting up a local development environment to submitting a skill for certification and expanding its power by linking it to other accounts. We have locally tested a skill to determine whether it behaves as expected, and also tested it in the service simulator on the Developer Console. We have even deployed it to an Alexa-enabled device. We also have gone over how to implement persistence in a skill so that users will be able to access saved information. In this final blog in the series, we’ve discussed how to link a skill to accounts for other services and make it even more useful. Let us know what you’ve built in the comments!
Clients leave when they feel that their needs aren’t being met. The strength of your client-partner relationship relies on a deep understanding of your client’s...
Responding to change is a key tenant of Agile Software Development. Predicting change is difficult, but software can be developed in a manner that...
A lot of this confusion surfaces around the scope of machine learning. While there is a lot of hype around deep learning, it is...