Command-line SMS script

Published: 2010-03-23
Updated: 2010-03-30, 2010-05-01, 2011-06-10

Note

The current version of this app is written in CoffeeScript and is published in the npm package registry.

A simple Python command-line script I wrote to send SMS messages using Clickatell’s HTTP API. In addition it:

  • Records sent messages in a log and has command option to view log file.
  • Has command options to query the Clickatell account balance and the status of previously sent messages.
  • Configuration can include a list of phone numbers.

Inspired by http://www.webmaster-forums.net/web-programming-and-application-development/send-sms-python-shell-clickatell-gmail


Updates

30-Mar-2010 (version 0.2.0)
  • Added phone book functionality (see -p command option).
  • Now works on Windows.
1-May-2010 (version 0.2.1)
  • Include phone book name in log.
10-Jun-2011 (version 0.2.2)
  • Enabled long messages.
8-Jul-2011 (version 0.2.4)
  • Changed conf file format to JSON.
  • Synced to CoffeeScript version.

Example usage

$ python sms.py 64912345667 "Hello World"
ID: 26a8147fa04ed9fj2a9ad125c55cee00

$ sms.py -s 26a8147fa04ed9fj2a9ad125c55cee00
apiMsgId: 26a8147fa04ed9fj2a9ad125c55cee00 charge: 0.8 status: 004
(received by recipient)

$ python sms.py -b
Credit: 204.900

$ python sms.py --help

NAME
    sms.py - Send SMS message

SYNOPSIS
    sms.py PHONE MESSAGE
    sms.py -s MSGID
    sms.py -b | -l

DESCRIPTION
    A simple Python command-line script to send SMS messages using
    Clickatell's HTTP API (see http://clickatell.com).
    Records messages log in /home/srackham/sms.log
    Reads configuration parameters from /home/srackham/.sms.conf

OPTIONS
    -s MSGID
        Query message delivery status.

    -b
        Query account balance.

    -l
        List message log file using /usr/bin/less.

    -p
        List phone book.

AUTHOR
    Written by Stuart Rackham, 

COPYING
    Copyright (C) 2010 Stuart Rackham. Free use of this software is
    granted under the terms of the MIT License.

Configuration

Set the Clickatell account configuration parameters in the sms.py script or put them in a JSON format file named .sms.conf in your home directory. For example:

{
  "USERNAME":  "foobar",
  "PASSWORD":  "secret",
  "API_ID":    "123456",
  "SENDER_ID": "+64912345678",
  "PHONE_BOOK": {
    "tom":   "+64 21 1234 5678",
    "dick":  "+61 25 1234 567",
    "harry": "+64 9 1234 346"
  }
}

sms.py source code

#!/usr/bin/env python

# A simple Python command-line script to send SMS messages using
# Clickatell's HTTP API (see http://clickatell.com).

'''
NAME
    %(prog)s - Send SMS message

SYNOPSIS
    %(prog)s PHONE MESSAGE
    %(prog)s -s MSGID
    %(prog)s -b | -l

DESCRIPTION
    A simple Python command-line script to send SMS messages using
    Clickatell's HTTP API (see http://clickatell.com).
    Records messages log in %(log)s
    Reads configuration parameters from %(conf)s

OPTIONS
    -s MSGID
        Query message delivery status.

    -b
        Query account balance.

    -l
        List message log file using %(pager)s.

    -p
        List phone book.

AUTHOR
    Written by Stuart Rackham, <srackham@gmail.com>

COPYING
    Copyright (C) 2010 Stuart Rackham. Free use of this software is
    granted under the terms of the MIT License.
'''
VERSION = '0.2.4'

import urllib
import os, sys, time, re
import simplejson as json

# Clickatell account configuration parameters.
# Create a separate configuration file named `.sms.conf` in your `$HOME`
# directory. The configuration file is single JSON formatted object with the
# same attributes and attribute types as the default CONF variable below.
# Alternatively you could dispense with the configuration file and edit the
# values in the CONF variable below.
CONF = {
    'USERNAME': '',
    'PASSWORD': '',
    'API_ID': '',
    'SENDER_ID': '',  # Your registered mobile phone number.
    'PHONE_BOOK': {},
}


PROG = os.path.basename(__file__)
HOME = os.environ.get('HOME', os.environ.get('HOMEPATH',
       os.path.dirname(os.path.realpath(__file__))))
LOG_FILE = os.path.join(HOME,'sms.log')
CONF_FILE = os.path.join(HOME,'.sms.conf')
if os.path.isfile(CONF_FILE):
    CONF = json.load(open(CONF_FILE))
SENDER_ID = CONF['SENDER_ID']
PHONE_BOOK = CONF['PHONE_BOOK']
PAGER = os.environ.get('PAGER','less')

# URL query string parameters.
QUERY = {'user': CONF['USERNAME'], 'password': CONF['PASSWORD'],
          'api_id': CONF['API_ID'], 'concat': 3}

# Clickatell status messages.
MSG_STATUS = {
    '001': 'message unknown',
    '002': 'message queued',
    '003': 'delivered to gateway',
    '004': 'received by recipient',
    '005': 'error with message',
    '007': 'error delivering message',
    '008': 'OK',
    '009': 'routing error',
    '012': 'out of credit',
}

# Retrieve Clickatell account balance.
def account_balance():
    return http_cmd('getbalance')

# Query the status of a previously sent message.
def message_status(msgid):
    QUERY['apimsgid'] = msgid
    result = http_cmd('getmsgcharge')
    result += ' (' + MSG_STATUS.get(result[-3:],'') + ')'
    return result

# Strip number punctuation and check the number is not obviously illegal.
def sanitize_phone_number(number):
    result = re.sub(r'[+ ()-]', '', number)
    if not re.match(r'^\d+$', result):
        print 'illegal phone number: %s' % number
        exit(1)
    return result

# Send text message. The recipient phone number can be a phone number
# or the name of a phone book entry.
def send_message(text, to):
    if to in PHONE_BOOK:
        name = to
        to = PHONE_BOOK[to]
    else:
        name = None
    to = sanitize_phone_number(to)
    sender_id = sanitize_phone_number(SENDER_ID)
    if sender_id.startswith('64') and to.startswith('6427'):
        # Use local number format if sending to Telecom NZ mobile from a NZ
        # number (to work around Telecom NZ blocking NZ originating messages
        # from Clickatell).
        sender_id = '0' + sender_id[2:]
    QUERY['from'] = sender_id
    QUERY['to'] = to
    QUERY['text'] = text
    result = http_cmd('sendmsg')
    now = time.localtime(time.time())
    if name:
        to += ': ' + name
    print >>open(LOG_FILE, 'a'), \
        'to:     %s\nfrom:   %s\ndate:   %s\nresult: %s\ntext:   %s\n' % \
        (to, sender_id, time.asctime(now), result, text)
    return result

# Execute Clickatell HTTP command.
def http_cmd(cmd):
    url = 'http://api.clickatell.com/http/' + cmd
    query = urllib.urlencode(QUERY)
    file = urllib.urlopen(url, query)
    result = file.read()
    file.close()
    return result

if __name__ == '__main__':
    argc = len(sys.argv)
    if argc == 3:
        if sys.argv[1] == '-s':
            print message_status(sys.argv[2])
        else:
            print send_message(sys.argv[2], sys.argv[1])
    elif argc == 2 and sys.argv[1] == '-b':
        print account_balance()
    elif argc == 2 and sys.argv[1] == '-l': # View log file.
        os.system(PAGER + ' ' + LOG_FILE)
    elif argc == 2 and sys.argv[1] == '-p': # List phone book.
        for i in PHONE_BOOK.items():
            print '%s: %s' % i
    else:
        print __doc__ % {'prog':PROG,'log':LOG_FILE,'conf':CONF_FILE,'pager':PAGER}
About these ads

One Response to “Command-line SMS script”

  1. Cillia johnson Says:

    Thanks for sharing this code, I will sign up for a Clickatell account and give it a try. I understand they have a trial period, so that’s good enough for me.

    Thanks again.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: