# BulkSMS.co.uk Python Implementation

Release 0.1
16th October, 2003

© 2003 David M. Wilson <dw-BulkSMS@botanicus.net>

All rights reserved except those specifically granted under the accompanying license. Please see the file `LICENSE` from the source distribution.

## Contents

1. What is BulkSMS?
2. How do I use the Python module?
3. How do I use the command-line interface?

## What is BulkSMS?

### BulkSMS.co.uk

BulkSMS.co.uk is a commercial SMS transit provider allowing programmatic transmission of SMS through an HTTP RPC API. The service provides facilities for sending 7-bit, 8-bit, and Unicode-encoded SMS messages, alongside reporting and account control functions.

More information on BulkSMS can be found at http://www.bulksms.co.uk/.

### BulkSMS Python Module

The BulkSMS Python Module is an object-based implementation of a BulkSMS.co.uk API client designed for Python 2.3. It has been designed for use with version 1.0 of the BulkSMS API.

More information on Python can be found at http://www.python.org/.

### BulkSMS Command Line Interface

The BulkSMS Command Line Interface is a small program written around the BulkSMS Python module, giving the user an easy interface for sending SMS messages using any command line from which Python may be run.

## How do I use the Python module?

### Module API Reference

Like all Python code, the module is self-documenting. Documentation for the module can be extracted into an HTML file using the the standard `pydoc` utility that is distributed with modern versions of Python. Alternatively, you may use the `help()` built-in to explore the module interactively:

`> import BulkSMS
> help(BulkSMS.format_credits)
...`

### General Overview

The module defines a `BulkSMS` class through which the client program performs all its operations. The class defines 4 methods for sending, reporting, quoting, and checking your account balance. These methods are `send_sms()`, `get_report()`, `quote_sms()`, and `get_credits()` respectively. Their interfaces are discussed at greater length in the generated documentation.

Error reporting is handled using the standard Python exceptions mechanism. 10 exceptions are defined, all derived from a single base `BulkSMSException` exception for simple exception filtering. Although each exception contains it's own error-specific values, calling `str()` on an instance of one of these exceptions is sufficient to create an error message suitable for a human.

A single helper function is provided, `format_credits()`, which simply returns a string representation of the given floating point number. The string representation is formatted as the credit read-out is formatted in the BulkSMS.co.uk member area.

### A Simple Example

In order to utilise the BulkSMS class, you must first instanciate it. You do this by calling a constructor, passing your BulkSMS username and password in the process:

`> s = BulkSMS.BulkSMS("username", "password")
`

A new instance has now been created. First, let's query our account to see how many credits we have:

`> credits = s.get_credits()
> print "Credits:", BulkSMS.format_credits(credits)
Credits: 392.95`

Okay, so we have plenty of credit available on our account. Now we would like to send a message to ourselves. We want our message to appear to have originated from `Mail Software`, and to include a count of all the messages in our inbox. The module `EmailBox` used below is hypothetical:

`> import EmailBox
> inbox = EmailBox.Inbox()
>
> to = [ "447967123123" ]
> from = "Mail Software"
> msg = "You have %d new messages." % len(inbox)
>
> msg_id = s.send_sms(to, msg, sender = from)
> print msg_id
7738925`

Now we would like to find out if our message has been delivered to our phone yet. To do so, we make use of the `get_report()` method. `get_report()` returns a list of 3-tuples, one for each recipient we specified in `to`. Since we only sent a message to ourselves, the return value of `get_report()` in this instance is a list of one 3-tuple:

`> report = s.get_report(msg_id)
> report
[('447967123123', 10, 'Delivered upstream')]`

As you can clearly see, the SMS is en-route to the destination number. The second item in the 3-tuple is the BulkSMS status code. It may be used for safely testing whether a message has reached it's destination:

`> for number, code, desc in report:
>
> if code == 11:
> print recipient, "got the message."
>
> else:
> print "%s: %s." % (number, desc)
>
447967123123: Delivered upstream.`

Time for a little risk assessment. Depending on what route you use, certain recipients may cost more to deliver to than others. Let's find out how much an SMS would cost using BulkSMS's best routes.

We can specify the route in one of two ways, either when calling `send_sms()` or `quote_sms()` by passing them the `cost_route` named parameter, or by setting the `cost_route` class variable. Here we set the class variable:

`> s.cost_route = 3
> to = [ "447967123123", "447967321321" ]
>
> s
BulkSMS('username', 'password', cost_route=3)
> s.quote_sms(to, msg, sender = from)
3.0
`

### Notes

It is worth note that the `send_sms()` and `quote_sms()` methods take exactly the same parameters. If you are testing your code, you may swap calls to `send_sms()` with calls to `quote_sms()`. Any errors that would occur when using `send_sms()` will still be caught and returned as exceptions, whilest saving credits.

You may also use this to save on keystrokes. For example:

`> def send_if_affordable(server, *pargs, **nargs):
> cost = server.quote_sms(*pargs, **nargs)
>
> if is_affordable(cost):
> return server.send_sms(*pargs, **nargs)
>
> else:
> raise CheapSkateError("it costs too much!")`

The `BulkSMS` class will return valid Python syntax for recreating itself if `repr()` is involked on it. This is useful for testing and debugging, but it does expose your password.

For this reason, a `secure_repr` class variable exists, which will cause the class to return a safe representation of itself. It is recommended that you set `secure_repr = 1` in production environments, where a string representation of the server object may leak out, for example, in a CGI backtrace.

`> s
BulkSMS('username', 'password')
> s.sender = "Default Sender"
> s
BulkSMS('username', 'password', sender='Default Sender')
> s.secure_repr = 1
<instance of BulkSMS.BulkSMS with id 1078290636>`

Recipient numbers at BulkSMS take the form `CCNNNN`, where `CC` is the 2 or 3-digit country code (without a zero prefix), and `NNNN` is the variable-length recipient number. Each number may be up to 14 digits in length.

The BulkSMS `sender` field may be up to a 14 digit number, as above, or an 11 character string. Further restrictions apply, please see the BulkSMS API reference.

## How do I use the command-line interface?

Designed with the power user in mind, the BulkSMS command line interface provides a simple, reliable method of sending SMS messages from your workstation, inside automated scripts, or from external calls by other programming languages.

No configuration of the command is required, however there are 3 environment variables which affect the command's operation. `BULKSMS_USERNAME`, `BULKSMS_PASSWORD`, and `BULKSMS_SENDER` may be set from your login profile to speed up the use of the command. By setting these variables, you no longer have to provide your username, password, and mobile telephone number each time you call the command.

Just like the Python API it wraps, the `sms` command also has 4 methods:

`sms send [options] "Message here." recipient1 recipient2 recipient...
sms quote [options] "Message here." recipient1 recipient2 recipient...
sms report [options] msg_id [recipient]
sms credits [options]`

I will not spend any more time going over the interface; it is too simple. Type `sms` at a command line to see the help page. It has all you need to know.

### Configuring Your Address Book

The command-line interface supports a very simple keyword-based phone book stored in your user profile or home directory. At startup, it searches for a `.sms_phonebook` file in the directories specified by the `HOME` and `USERPROFILE` environment variables.

If such a file is not found, an empty one is created. The format of the file is simple: one line per record, one or more recipients per keyword, specified in the following format:

`keyword1: recipient1, recipient2
keyword2: recipient1
keyword3: recipient1, recipient2, recipient3`

And so on. Once you have populated this file, you may use a keyword from the file in place of a recipient number, or a list of recipient numbers. Here is an example:

`dw: 447967123123
boss: 447967321321
dw+boss: 447967123123, 447967321321
friend1: 447900111222
friend2: 447910113222
friends: 447900111222, 447900113222`

With this phone book, you may use a command such as this:

`$ sms send 'Hello myself, my boss, and my friends!' dw boss friends
Message sent. Message ID: 7738925
$ sms report 7738925
447967123123 10 Delivered upstream
447967321321 10 Delivered upstream
447900111222 11 Delivered to mobile
447900113222 10 Delivered upstream`