Making An SMS Chatbot #2 – Simple Natural Language Processing

Now that we have a way of sending and receiving SMS messages, let’s put it to use with some natural language processing!

Tokenize and Stem

The first step towards any sort of natural language processing is normalizing the input, as to make it easier to understand, or at the very least, manipulate. In our case, we want to split the input into words, and then remove any alteration, as to bring them back to their most simple form, or stem. That means removing plural signs and verb conjugations, amongst other things. For this, I used the Apache OpenNLP library.

Algorithm

Now that our input is “tokenized”, it can be used to issue commands. To keep things simple, the chatbot will respond to commands with a predefined syntax. Possible commands will be registered with an expected word sequence, and will then be invoked when the corresponding sequence is received. When the program starts, a command tree is created, which can then be navigated, word by word, to find the associated method call.

The input message “Say hello.” will cause a line to be printed to the console.

To allow for more variation, all extraneous words are ignored. For instance, “Keep saying a very grateful hello forever.” will resolve to the same method.

Parameters

The algorithm itself is quite simple, but can be expanded to make it a bit more powerful. For instance, passing parameters could be a nice addition. If those could be properly typed, that would be even better.

Using reserved keywords when registering a command, I can define parameters to be passed onto the invoked method. These keywords are user-defined, therefore completely customizable. At the moment, I have included most basic types, namely Integer and String, but in the future, one might see a command similar to “track package <UPS-tracking>”, or even “what is $ <int> in <currency>?”.

Here is what a command declaration looks like in Java.

Handling User Identities

Since all commands are tied to a phone number, we can use it to do some basic user authentication. In fact, it would be even nicer to have users belong into “groups”, or “roles” which then determine the commands they are allowed to access. When a user invokes a forbidden command, it can simply be ignored.

Users are saved to a MySQL database, using Hibernate ORM.
To make manipulating permissions easier, I added a CommandContext object which can be used in any command method to get contextual information, which includes the user information.

“Conversational” Commands

Repeating several times in a row “Say hello 5 times” can become somewhat tedious. A “say that again” command could be useful.

In fact, it would be great if our chatbot, like most modern assistants, had some contextual or conversational awareness. That way, we could define commands to be only accessible after a particular relevant command has been invoked. For instance, “Yes” and “No” commands could be enabled after a question, as part of the ongoing conversation.

This is achieved by using a “@Conversational” annotation on the new command, which defines the method call after which it should be enabled. Then, when a new message is received, the conversational commands are checked first for a match. This can be used as an effective way of “masking” default commands depending on the context.

Further Improvement

As-is, the bot “works”, but does not have any useful functionality. You can say “hello” to introduce yourself, which will create a new user in the database. It can remember names too, which could theoretically be used to customize messages. There are, however, several issues which should be addressed before going further.

  • The database access is NOT thread-safe. The bot works, as long as there is only one concurrent user.
  • The actual command tree-searching methods are getting quite complicated. The class itself could definitely be more finely divided to improve readability.
  • The goal is to, someday, listen on multiple communication endpoints. For instance, the bot could be used as a Skype or Messenger bot. This should definitely be feasible in its current implementation. We could probably go a step further, and use a proper SMS service, instead of one I hacked together using a spare iPhone 3GS.
  • Right now, defining synonyms, or aliases for commands is quite clunky. It requires creating a brand new method, with its own @CommandHandler annotation. Allowing arrays or some other way to define aliases would be very useful. The same can be said of conversational commands.
  • The bot needs a friendly name!

See on GitHub.

Making an SMS Chatbot #1 – Exposing a REST API on an iPhone 3GS for sending and receiving SMS

This is a fun little project I started while toying around with a spare iPhone. All mentioned code can be found on my GitHub.

Setup

For this project, I will be using a jailbroken iPhone 3GS running iOS 6.1.6, the latest it can handle. The nice thing about these older devices is that they can be easily restored to any compatible iOS version, without any trouble. Jailbreaking then allows us to get really creative with the software running on the machine. I used the redsn0w jailbreak for the new-bootrom iPhone 3GS, with the p0sixspwn package to make it an untethered jailbreak.

Going Online

The most important part of a web API is, quite obviously, the web part. For this purpose, I used the ios-webstack.tk Cydia repository which provides a “one-tap solution” for installing a complete WebStack on a jailbroken iOS device. The repository actually contains multiple network service packages, such as an SMTP server, and a fully-fledged WordPress installation. The part we’re more interested in, however, is the ios-lighttpd-php-mysql package, which installs, amongst other things, an HTTP daemon and a convenient preferences panel in the settings app to enable or disable the service.

I configured httpd to execute CGI scripts, and set a filetype association for .sh and .py scripts to their respective interpreters. That way, when the server gets a request for a python file, it will instead execute it and return the console output as a raw HTTP response. This will allow us to make quick and dirty scripts for running on the very resource-limited hardware that we have.

Sending Messages

For sending out SMS messages, I could not find any built-in or third-party options that were dedicated to operating the messaging app from the command line. That said, I did find a quite obscure and undocumented use for the biteSMS app that will allows me to send messages from a shell script. The app itself is riddled with ads, but luckily we won’t be using its GUI. Once installed, we can call it from a shell prompt to send a text message!

/Applications/biteSMS.app/biteSMS -send -carrier 1234567890 <my message here>

 Receiving Incoming Transmissions

Now comes the fun part. Ideally, we would like a way of triggering an action whenever an SMS is received. In the best-case scenario, we would be calling a shell script directly. Worst-case, we would end up catching some UI notification, which would be somewhat impractical, be we could work with that.

I looked into Activator triggers for incoming messages, sadly the triggering action was not as reliable as I would have liked. The iPhone ended up missing many text messages, or only triggering when multiple unread messages had started piling up. In the process however, I did find ways of triggering UI system functions, like capturing a screen shot, from the command line, which is nice, but not technically useful for the task at hand.

 How the iPhone stores messages

While digging around, I stumbled upon the internal SMS database, which is simply an sqlite3 database. The .db file itself is located in /var/mobile/Library/SMS/sms.db. In there, we find a “chat” table containing the conversation metadata, and a “message” table, which contains the actual text messages. It is then a matter of writing an SQL query to grab that data.

SELECT m.rowid, c.chat_identifier,m.is_from_me,m.date,m.text FROM message m INNER JOIN chat_message_join cmj ON cmj.message_id=m.rowid INNER JOIN chat c ON cmj.chat_id=c.rowid WHERE cmj.message_id=:id

To trigger an action when a message is received, I wrote a little background script which polls the database every second, checking whether a new entry has been created by looking at the highest id assigned. When a change is detected, the script makes an HTTP request to my computer, which runs the backend software.

The iPhone, like OS X, uses the plist format with launchd to control system services. The syntax is somewhat more esoteric than the more common systemd, but it gets the job done anyway. The service files are located under /Library/LaunchDaemons, and can be enabled using launchctl.

Other comments

  • To prevent the iPhone from turning off its Wi-Fi connection after going to sleep, I used the Insomnia Cydia tweak.
  • Instead of going through the process of installing a recent python version, which would have included proper JSON serialization support, I formatted the HTTP response manually by concatenating various parts of the SMS data. Using proper JSON formatting would definitely be better, especially in the long run, but I did not really want to go through the trouble of installing multiple python versions on the iPhone.

I now have a way of sending and receiving SMS by sending a few JSON-formatted requests. That should definitely come in handy later…