UPDATE 12/1/2016 8:28am:
Funny enough, while debugging, we discovered that Alexa does natively provide this functionality :( So, use this skill as a learning experience and potential resource - but I guess there will be no skill store submission!
Story - or why this skillBeing big movie fans, and now that we got an Amazon Echo at AWS Re:Invent, we cannot wait to make the two work even better together.
We both love going to the movies, and what's most natural than asking Alexa about the latest movies, and getting links to buy tickets directly to your phone?
It seems this is a skill Alexa didn't possess at this point, thus we decided to bring it to live for this context.
Submitted Skill ID #
amzn1.ask.skill.a5f9195b-363e-4a34-bf80-940ac48ce41e
Entry VideoProject and ArchitectureDespite the limited (free) time we have on our hands at the conference, we decided to give this skill idea a shot. At first, this project seems perfectly suited for Alexa Skills Kit integration, and Natural Language Processing, leveraging some API to get movie showtimes and theater information.
We leveraged the alexa-color-sample as an initial scaffold for our Alexa first skill test, and it worked perfectly out of the box. From there, we did start taking our AWS Lambda function to the next stage, to convert it to TypeScript and adding more logic into it.
Very fast, we noticed there was no obvious API for getting movie showtimes for the US market. After few investigations and an unfulfilled draft developer account request to Fandango, we decided to take the matter in our own hands and do a scrubbing layer over an already existing successful integration, here over Microsoft's Bing search engine. This search engine already handles getting a list of movies currently being shown in local theaters, as well as redirecting to Fandango for the final ticket purchase experience. This seemed the perfect set of data to power our new skill for Alexa.
The current architecture, as seen below, is very basic. It is leveraging Alexa Skills Kit as well as Lambda Function to serve our Skills requests, or intents. Our Lambda function build over its VUI flow the search criteria needed to scope our movie showtime selection further down, using natural language as much as possible. Once the final movie, movie showtime and theater is selected by the user using Alexa, a matching Fandango URL is dispatched by the AWS Lambda function to the user, to finalize the purchase (and/or further sharing) of the movie tickets.
The data flow is initiated by the Get Tickets skill being awaken, with or without context as seen in the VUI diagram below. The Lambda function is then triggered with known intents, to help negotiating the missing scoping parameters, by asking in return further questions or usage reminders to the user.
VUIIntent Schema
{
"intents": [
{
"intent": "FindMovies",
"slots": [
{
"name": "Genre",
"type": "GENRE"
},
{
"name": "City",
"type": "AMAZON.US_CITY"
}
]
},
{
"intent": "GetDetails"
},
{
"intent": "FindShowtimes"
},
{
"intent": "SetDate",
"slots": [
{
"name": "Date",
"type": "AMAZON.DATE"
}
]
},
{
"intent": "SetTime",
"slots": [
{
"name": "Time",
"type": "AMAZON.TIME"
}
]
},
{
"intent": "AMAZON.HelpIntent"
},
{
"intent": "AMAZON.NextIntent"
},
{
"intent": "AMAZON.StopIntent"
}
]
}
Sample UtterancesMultiple sentences and sentence fragments are listed, to make the interaction with the Get Tickets skills as natural as possible.
FindMovies the {Genre} movies
FindMovies What {Genre} movies are playing
FindMovies Show {Genre} movies
FindMovies Get {Genre} movies
FindMovies Get {Genre}
FindMovies Show {Genre}
FindMovies show {Genre} films
FindMovies {Genre}
FindMovies {Genre} movies
FindMovies {Genre} films
FindMovies movies
FindMovies films
FindMovies top films
FindMovies top movies
FindMovies top {Genre} movies
FindMovies movies in {City}
FindMovies films in {City}
FindMovies {Genre} movies in {City}
FindMovies What movies are playing in {City}
FindMovies find movies
FindMovies find {Genre} movies
FindMovies find {Genre}
FindMovies find {Genre} films
FindMovies what's playing
FindShowtimes get show times
FindShowtimes get movie times
FindShowtimes get times
FindShowtimes get tickets
FindShowtimes show times
FindShowtimes tickets
SetTime {Time}
SetDate {Date}
SetLocation {City}
GetDetails what is it about
GetDetails tell me about it
GetDetails what is it
GetDetails synopsis
GetDetails summary
GetDetails what is the synopsis
GetDetails what is the summary
GetDetails tell me what it is
GetDetails what is it
GetDetails get details
GetDetails details
Genre typeThe movie genre type is defined as a Custom Slot Type, with the following possible values:
Action
Adventure
Animated
Biography
Comedy
Crime
Documentary
Drama
Family
Fantasy
History
Horror
Musical
Mystery
Romance
Sci-Fi
Science Fiction
Sport
Suspense
Thriller
War
Western
It is an optional field for our skill, but helps targeting down the movie list tremendously :)
ScrubbingThe data is scrubbed from the Microsoft Bing html search page, leveraging Cheerio, offering easy and fast DOM parsing in our AWS Lambda function. This functionality doesn't seem exposed to the Bing Search API, thus making scrubbing a necessary step.
The scrubbing is abstracted behind the following data APIs:
/**
* Fetch all movies for a given array of search fields
*
* @param {string} [movieGenre] - The genre of the movie, mapped to the
* GenreType enum, if any.
* @param {string} [city] - The city to be used as scope, if any.
* @returns {Promise<Array<IMovie>>} - The matching results, if any.
*/
getMovies(movieGenre?: string, city?: string): Promise<Array<IMovie>>;
/**
* Fetch the movie details for a given movie instance
*
* @param {IMovie} movie - The movie to be scoped to.
* @param {string} [date] - The day the showtimes are to be scoped to, if any.
* Expected format is yyyy-MM-dd.
* @param {string} [time] - The time of day the showtimes are to be scoped to,
* if any. Expected format is HH:mm.
* @returns {Promise<IMovieDetails>} - The movie details if found.
*/
getMovieDetails(movie: IMovie, date: string, time: string): Promise<IMovieDetails>;
Those APIs are performing at a good-enough performance level, to not be of a visible impact to the end user. In future, they could leverage an MRU caching mechanism, or even a REDIS caching layer as they are highly invariant.
City vs. City+State considerationsAfter multiple tests scoping our Get Tickets intent search to a given city, we noticed the city alone wasn't precise enough to get valid showtime results. It needed city as well as state, since the city alone was ambiguous. To resolve this and to keep user-ease-of-use in mind, we did include an implicit mapping of the top 300 US cities to automatically transform city to {city + state} to help driving the appropriate showtime results more intuitively to the end-user.
Usage & Getting startedPlease visit our project's GITHUB page to get all the information needed to get started.
Future workIt is worth noting the AWS SNS and AWS DynamoDB integrations are for future development, respectively for sending the final purchase URL to the user via a SMS text message, and for storing user preferences, such as favorite city and/or zipcode, as well as favorite theater, favorite movie genres and phone number. While the current Get Ticket application leverages a 'card' for the movie being chosen, it doesn't allow the user to copy & paste or click through an hyperlink to complete the purchase - thus the use of SNS for sending an SMS message with this information.
The DynamoDB is to be used as persistent storage (as compared to the session-level storage offered by the Alexa Skills Kit for the skill) to store data across multiple sessions of the Get Tickets skill. Those can greatly help 'remembering' user past interactions, to help streamlining the Get Tickets experience.
Update 1:
The AWS SNS integration is done in its initial form. The phone number is at this stage hardcoded in the codebase, but a SMS with the ticket purchase link is sent. The next (future) step will be to have Alexa capture your phone number, and potentially verifying it (using AWS SNS confirmation APIs) and persisting it (using AWS DynamoDB).
Update 2:
See top-level header in this project for an unfortunate discovery
Comments