Back to Parent

Outcome


Overview

HotelBot Andrew is an SMS bot that will help users search for nearby hotels.

At its core, a user can text Andrew to search for nearby hotels and it will respond with a short list of possibilities. From text messages, it is able to extract both core criteria, such as the city and dates of arrival, and other criteria that the user may optionally provide, such as the duration of stay, minimum rating (in terms of stars), and maximum price. Utilizing the allmyles API - for this prototype, just the staging or development version - Andrew then finds up to 30 matching hotels, returning them to the user in sets of 3. (Think Google searches: a search returns thousands or millions of results, but only 10 are displayed at once.)

From here, the user can request to see images, descriptions, and links for each of the returned hotels - or see the next set of 3 results. All resources are delivered in-text, e.g. images are embedded as an MMS instead of linked to. Some advanced features that were originally proposed did not make it into this final project.

However, as it is Andrew is a useful bot for travellers who need to scout out potential places to stay at in the future, intended as a more lightweight and natural alternative to other hotel search apps or sites. Importantly, Andrew is a bot for looking for hotels, not booking them. While Andrew could be extended to provide booking functionality, as is supported by the allmyles API, care would have to be taken so that booking is reliable and secure; this would be a direction for future work.

Features

The following matrix is a copy of one given in the project proposal, categorizing desired features in terms of their priority towards a working bot and their effort to implement. Features marked (D) are done, those marked (N) were not implemented, and those marked (C) could not be implemented due to API restrictions.

Andrew priority matrix
Show Advanced Options

Workflow

Andrew's workflow can be divided into three stages, with the first two shown in the "Search Workflow" diagram and the last shown in the "Info Workflow" diagram. In the first stage, the user has not provided any info to Andrew - either the user just greeted the bot, or reset the workflow by ending the conversation or explicitly resetting it. Andrew will give an introductory explanation if the user greeted it, which will prompt the user to provide the location and date of the potential stay.

If Andrew is able to parse the location and date from the user's next text, the workflow moves to the second stage. Here, Andrew prompts the user for any additional criteria he/she might have (currently supported are the number of nights, minimum rating, and maximum price). When the user provides these or says that there aren't any, Andrew will go off to conduct the search, but not before letting the user know the results may not come immediately, since in this prototype the API call takes some time. If the API call succeeds and at least one hotel passes the user's additional criteria, Andrew will list the up to the first 3 results.

As the workflow moves to the final stage, Andrew lets the user know what the user can do in this stage. Currently, the prototype supports the user asking for hotel images, descriptions, and links (to the hotel website), in addition to asking to display the next 3 results. Andrew will fulfill as many requests as the user wants, provided that he can parse the request type and indicated hotel from the text; this final stage is a loop.

At any point in the entire workflow, the user can text Andrew for help, and he will respond with an info message depending on which stage the user is currently in. The user can also reset the workflow at any point, either by ending the conversation (e.g. saying "goodbye!") or by explicitly asking Andrew to restart (e.g. "let's restart").

Search workflow%281%29 1
Show Advanced Options
Info workflow%281%29 1
Show Advanced Options

Personality

If Andrew were a real-life person, he would be a hotel concierge, albeit a "global" concierge able to help you with any hotel. As someone with more than a decade of experience in his job (and enjoys it, since this special concierge job lets him experience the world's best hotels), he is ever-ready to share his knowledge with users. While the name Andrew is Greek for "strong" or "manly", these are not necessarily the traits which this Andrew embodies; instead, the name references a personal friend who could do very well as a global concierge.

As a hotel concierge, Andrew is clean, polite, and professional. As a well-traveled man, he is confident in his knowledge, but at the same time is a reasonable man who knows what he can and can't do. When Andrew speaks, he does so with clarity and patience. For example, if the user asks for help, he won't mind repeating instructions which he might have uttered just seconds ago. While he has to keep a professional distance between himself and the user, he also strives to avoid being stiffly formal in a way which might alienate the user. Andrew is always thinking about how things that can go wrong, but as a busy concierge serving many users at once, he can't spend all of his time on any one of them. So if a user repeatedly makes bad requests, that user may start to notice a little bite in Andrew's response...

Technical Implementation

Andrew was developed with Twilio routing SMS messages to a Sinatra back-end server hosted on Heroku. The Sinatra app has a single endpoint, /incoming_sms, which handles and responds to all the text messages Twilio forwards. Sinatra in turn gets most of the data requested by the user from a travel API. Expedia was originally the API of choice; however, because there is a barrier to getting access to their API (one must be an Expedia affiliate), the allmyles API was used instead. allmyles provides a hotel API which accomplishes roughly the same thing for hotels that the Expedia API does, but there are some differences between the two. (This is why some features in the matrix are marked as could not implement.)

In addition to the gems used in the skills development exercises earlier in the course, two gems (found in the discovery phase of this project) were key in making a functional Andrew. The first is RestClient, a general-purpose gem for making calls to any RESTful API. RestClient was particularly useful because, unlike Expedia, the allmyles API does not have a Ruby gem-wrapper for it. The second is Chronic, a gem which can parse dates - even relative dates - from bodies of text. For example, if today is Friday, December 16th, Chronic would be able to tell that "I'm flying to Seattle next Monday" refers to Monday, December 19th. While Andrew implements the rest of the language processing himself, dates are the easiest thing for him to handle due to how effective Chronic is.

In the original proposal, an SQL database was only necessary to implement certain "bells-and-whistles" features. None of these were implemented, but a database became desirable for two unforeseen reasons. First, unlike the Expedia API, the allmyles API expects city codes (e.g. SFO for San Francisco) instead of city names when searching for hotels, so a database table was created to store city name-to-code mappings. Second, calls to the allmyles API tend to be lengthy - almost a minute in some cases - which means that Sinatra is unable to handle certain requests before the Twilio or Heorku timeouts. While Sinatra can reach back out to the user once the request is complete, it is no longer possible to store session state in cookies (session variables), so the database is used instead for this purpose.

The diagram below shows the mentioned tables. The City table maps city names to city codes. The State table is equivalent to a Sinatra session variable: for each mobile number (the "from" column) there is a row in the State table, if the user behind that number is in the second or final stage of the workflow. (In the first stage, there is no information to keep track of since the user is just starting.) A state row records the stage the user is in, the number of hotels shown so far and the maximum number of hotels Andrew has to show, the number of bad requests the user has made, the allmyles API cookie, and the user's initial search criteria - city and date. The Result table stores hotel search results for each active session: each row corresponds to a hotel and has basic information about it, and there may be up to 30 rows for each mobile number. Results are tied to a session and cleared when that session ends; it may be more efficient to pre-fetch and store them for future use, but results are ephemeral. That is, they are only intended for the user who conducted the search at the time the search was conducted. (This is part of the reason a cookie is needed for a supposedly RESTful API. For more information, see the allmyles documentation, "Quick Summary" and "Common Gotchas".)

Andrew data diagram
Show Advanced Options

Demo

The five screenshots below show an example conversation a user might have with Andrew. While it doesn't exhaust all of his functionality, it demonstrates how he can be useful.

Andrew demo 1
Show Advanced Options
Andrew demo 2
Show Advanced Options
Andrew demo 3
Show Advanced Options
Andrew demo 4
Show Advanced Options
Andrew demo 5
Show Advanced Options

Future Work

There are a couple of directions for future work, with the aformentioned booking functionality being one of them. Another direction would be to implement the medium/low priority features that weren't implemented in this prototype, such as allowing a user to ask Andrew to watch the price of a certain hotel and send alerts if it changes. However, likely the best thing would be to take its existing functionality and make it more robust. For example, Andrew could expand to be able to accept a greater variety of criteria (e.g. number of occupants) and display more information about each hotel (e.g. address). Fleshing out his search capabilities would enable him to compete with existing travel websites and apps. As a conversation-based interface, Andrew could also undergo some user testing to determine where his text parsing skills fall short, and how those areas could be improved.

Reflections

Overall, Andrew turned out well, and through his development I cemented some of my skills with particular systems (e.g. Ruby and databases) and gained valuable experience in tying together various platforms - Andrew is a system that includes Twilio, Heroku, Sinatra, SQL, and the allmyles API. His development did have a few bumps along the way. The most grief was caused by unforeseen API/platform barriers and restrictions. In one case, I found that it was unlikely that I would gain access to the Expedia API within this project's timeframe, so I had to spend time searching for another similar travel API (it turns out many of them are equally closed), and as a result of the differences between the new one (allmyles) and Expedia, I had to revise Andrew's feature set. In another case, I was unaware that Twilio imposed a 15 second time limit on responding to request, which forced me to change how Andrew remembered state (from session cookies to database tables) late into the project. While running into walls like these is a part of any development process - and getting past them is a constructive experience - I do think I would have been better off knowing the limitations of the platforms I was going to use before committing myself to them. All that said, I'm fairly satisfied with the Andrew's outcome, and I'm sure he is too.

Drop files here or click to select

You can upload files of up to 20MB using this form.