Send SMS messages from Prolog using Twilio

It would be really good to be able to send a text message to yourself if as a reminder for events, or if an automated task that you have running fails. There are services that do this for us, and all that is needed is an account and a REST call to send the message! Twilio is a popular sevrice and it is easy to set up, but how can we call Twilio services using Prolog?

To send an SMS it is pretty easy once you have setup a few things in Twilio. You will need the following:

  1. A Twilio account
  2. The Twilio SID number (your account number)
  3. A Twilio Authentication token
  4. A Twilio phone number, or service account number

You can set up a test account to get things working, details are here. I'm not going to go into great details into how to configure Twilio, they have their own well written documentation.

Now to send a message, the actual call is a simple HTTP POST request, and the data is send using form data, as if you were posting from a form on a webpage. SWI-Prolog has easy predicates to handle this kind of thing. The response that is returned contains details of the message, and most importantly the ID for the message that was sent. This comes back as JSON so it will need to be converted to a Prolog structure before reading.

To handle the POST request and the JSON conversion the following libraries will be used:

:- use_module(library(http/http_client)).
:- use_module(library(http/json)).

Best define some constants for the details that will be sent that identify your Twilio account, so that they are easy to change later.

twilio_sid('<Your Account SID>').
twilio_authtoken('<Your Auth Token>').
twilio_from('<The phone number or Service Id you are sending from>').

The message is sent as form data, and there are three parameters, the phone that the the message is 'from' (the sender, you), the phone that the message is 'to' (the reciever) and the 'body' of the message (the message!). There is a handy feature that we can define parameter like is as a list of =\2 predicates and the HTTP libraries can process them.

form_body(From, To, Message, Body) :-
    Body = [
        'From' = From,
        'To' = To,
        'Body' = Message
    ].

The final task is to do setup the authentication and do the call!

send_sms(To, Message, Result) :-
    % Get the account details
    twilio_sid(User),
    twilio_authtoken(Secret),
    twilio_from(From),

    % Create the form body
    form_body(From, To, Message, Body),

    % Add the auth and send the message
    format(atom(Path), 'https://api.twilio.com/2010-04-01/Accounts/~w/Messages.json', User),
    http_post(Path, form(Body), R, [authorization(basic(User, Secret))]),

    % Read the result
    atom_json_dict(R, Rj, []),
    Result = Rj.sid.

The code here is commented as to what each section does, but there are a couple of notes that took me a while to get right, so I want to point them out.

  • The authorization header is added as an option to the request, Twilio expects a basic auth header which is the account SID and Authentication Token. The docs for this are here.

  • To send form data, wrap the 'Data' parameter list in a form/1 term. This is not obvious until you figure it out, but the http_post\3 predicate can actually send various types of post data, so you need to specify. The docs are here. If you send the wrong type of post Twilio still responds but with a list of the messages that you have sent ... very confusing.

  • Phone numbers have to be in the E.164 International Phone Number Format which is basically <country code><area code><phone number>.

  • There is lots of information returned with the response, I only return the SID here, which is the ID for the message (I think!).

Ok, last thing, how do you send an SMS? Easy I'm going to use a fake Australian mobile number for the example.

send_sms('+61412345678', 'Hello, Twilio!', Result).

Too easy right?