Using Twitter to Build a SMS Service > Example Service: Echo
As an example, we'll create a service in Python that will echo back at you whatever you send to it.
For example, if you send the following message to Twitter's 40404 short code:
d echo "What's up?"
it will respond with:
ECHO: What's up? (reply? send: d echo hi)
The text in parentheses is added automatically by Twitter you can't get rid of it.
Start by creating a new Twitter user for your service. We chose echo for this example, but you will have to choose your own. For the purpose of simplicity, we will set its password to 'echo' as well.
Users can add the service as their friend and then send it direct messages, to which the service will respond.
The Echo service will use a Twitter interface class that we'll be creating. We'll go over that class first and then over the details of the Echo service itself.
To create your Twitter service, you'll need:
Python 2.4 or higher
The json-py module (called python-json in pypi) for JSON support (http://cheeseshop.python.org/pypi/python-json)
A Twitter account for your service (http://www.twitter.com/)
A code listing will be provided after we go over the basic functionality provided by the Twitter class.
Our Twitter interface provides most of the functions that you'll want to use to implement a Twitter service. Some of them are documented in the public API, but some of them are not. Here are the methods we'll be using:
get_followers (public): Gets a list of users that are following you. People who are following you will hear your messages. http://twitter.com/statuses/followers.json
get_friends (public): Gets a list of users that you consider your friends you'll receive their updates and their direct messages. http://twitter.com/statuses/friends.json
get_direct_messages (public): Returns every (non-deleted) direct message you've ever received. (Ideally there would be a function just to return unread messages, but it doesn't exist as of this writing.) http://twitter.com/direct_messages.json
befriend_all (undocumented): This function makes everyone who is following you your friend. This is important because in Twitter, you can only receive message from people who are your friends. http://twitter.com/followers/befriend_all
send_direct_message (public): Given a user name or id and a message to send, this method will send the message to the user - provided that user has accepted you as a friend. As noted in the API documentation, this method must be called using POST. http://twitter.com/direct_messages/new.json
delete_direct_message (undocumented): Lets you delete a message, given its message id. A service needs to delete messages every time it reads them to avoid acting twice on the same message. http://twitter.com/direct_messages/destroy/[MessageID]
delete_one_page_of_sent_messages (undocumented): Gets the Message IDs of all the sent messages that will show up on the user's sent direct message panel. It uses the IDs and repeatedly calls delete_direct_message. There might be a better way of doing this, but as of this writing it is the best mechanism we could find. http://twitter.com/direct_messages/sent(This is normal user-visible page, not an API call.)
twitter.py:
Code View: Scroll / Show All
#!/usr/bin/python import sys import os import urllib2 import urllib import json import time import re import random TWITTER_URL = "http://twitter.com" class Twitter(object): def __init__(self, useremail, password): authstring = "%s:%s"%(useremail, password) self.__twitter = urllib2.build_opener() self.__twitter.addheaders = [ ('Authorization', 'Basic %s'% (authstring.encode("base64").strip())) ] self.__sent_destroy_re = re.compile("/direct_messages/destroy/([0-9]+)") def get_followers(self): page = self.__twitter.open(TWITTER_URL + "/statuses/followers.json") return json.read(page.read()) def get_friends(self): page = self.__twitter.open(TWITTER_URL + "/statuses/friends.json") return json.read(page.read()) def befriend_all(self): page = self.__twitter.open(TWITTER_URL + "/followers/befriend_all") def get_direct_messages(self): page = self.__twitter.open(TWITTER_URL + "/direct_messages.json") return json.read(page.read()) def send_direct_message(self, friend_id, text): params = { 'text' : text, 'user' : friend_id, } page = self.__twitter.open(TWITTER_URL + "/direct_messages/new.json", data=urllib.urlencode(params)) return json.read(page.read()) def delete_direct_message(self, direct_message_id): page = self.__twitter.open(TWITTER_URL + "/direct_messages/destroy/%s" % direct_message_id) def delete_one_page_of_sent_messages(self): page = self.__twitter.open(TWITTER_URL + "/direct_messages/sent") for line in page.readlines(): m = self.__sent_destroy_re.search(line) if m: self.delete_direct_message(m.group(1)) def main(): #simple usage example twit = Twitter("username", "password") print twit.get_friends() if __name__ == '__main__': main()
Twitter requires that most commands be authenticated using Basic Authentication. The constructor builds an object that will attach the appropriate authorization header every time we make a request. The code inside the constructor for Twitter base64 encodes the text "username:password" and then puts it in an "Authorization" header prefixed with the word "Basic." You can read more about Basic Authentication at http://www.ietf.org/rfc/rfc2617.txt.
Almost every method simply uses the opener we created in the constructor to access one URL. The URL returns a JSON dictionary that we convert into a python dictionary.
The .json appended to the URL tells Twitter to return the results as JSON dictionaries. XML and RSS are supported for many methods as well. JSON was chosen here because it is lightweight and translates easily into Python dictionaries. All we have to do is call the read method of the JSON module on the results, and we get a Python dictionary back with all the results
A JSON dictionary for a get_friends call looks something like this (this user has one friend named testinos:
[{"name":"testinos","description":null,"location":null, "profile_image_url":"http:\/\/assets0.twitter.com\/images\/ default_image.gif?1181015407","url":null,"id":5719462, "screen_name":"testinos","protected":false}]
After calling json.read() on the text of the JSON, we get a Python dictionary that looks like this:
[{'name': 'testinos', 'url': None, 'profile_image_url': 'http://assets0.twitter.com/images/default_image.gif?1181015407', 'screen_name': 'testinos', 'location': None, 'protected': False, 'id': 5719462, 'description': None}]
There are a few methods that warrant some additional explanation:
The Twitter API requires that POST be used when sending direct messages. The opener knows to use POST instead of get when it's passed a urlencoded string as the second parameter to open. You'll notice in the send_direct_message function that we call urlencode on a parameter dictionary for just that purpose
The delete_one_page_of_sent_messages function has to load a normally user-visible web page in order to do its duty. This is because there is no API call to retrieve sent messages. A regular expression extracts the Message ID for each sent message out of the page and passes it to our delete_direct_message.
We'll use the commands described above to build our service. The Twitter API only supports polling right now, so we'll be doing everything in a loop.
echo.py:
#!/usr/bin/python import twitter import time twit = twitter.Twitter('servicename', 'servicepass') while True: twit.befriend_all() print "checking..." dms = twit.get_direct_messages() for dm in dms: twit.delete_direct_message(dm['id']) print "Echoing! %s" % dm['text'] twit.send_direct_message(dm['sender_screen_name'], "ECHO: " + dm['text']) time.sleep(120) twit.delete_one_page_of_sent_messages()
Once you have the Twitter interface written, the service itself is trivial. Every two minutes we befriend anyone who has added us as their friend, and check for new messages. If we have a new message, we get the contents of it, delete it, and then send it back to the originator prefixed with ECHO:.
To try this out for yourself:
Edit echo.py to contain the username and password for your service.
Create a test Twitter account or a personal one.
Follow your service (Follow is a Twitter concept similar to making someone your friend, except it is a one-way relationship, instead of two-way)
Copy twitter.py and echo.py into the same directory on any machine that meets the requirements discussed at the start of the chapter, then type:
python echo.py
Now have a friend send a direct message to your service (e.g., "d echo this is a test").
Have fun tweating!