Imagine, if you will, a baffling business establishment which
- Sells the most delicious cupcakes you’ve ever had
- Sells these cupcakes out of a mobile cupcake truck
- Has no single permanent location
- Only gives out clues to its location at any given moment via Twitter
My friends, I give you Cupcakory.
Now, I just wanted to be sent an email whenever Cupcakory tweeted something new. Is this not possible in Twitter? Twitter can send you an SMS on new tweets, and the new tweets of course show up on your homepage if you’re a follower, but I don’t use text messages or Twitter, so those didn’t work for me.
So here’s a simple Python script I wrote to do what I needed. The script looks at the most recent tweet by a Twitter user and compares it to the most recent it knows about. If it’s a new tweet, the script will send the contents in an email. Works well with cron to periodically check for new tweets.
(Mostly for my future benefit, here’s a quick walkthrough of much of the script)
8 # get the most recent tweet from Twitter 9 page = urllib2.urlopen("http://twitter.com/cupcakory") 10 soup = BeautifulSoup(page) 11 contentTags = soup.findAll('span', attrs={"class" : "entry-content"}) 12 twitterTweet = contentTags[0].string
All the heavy lifting in the script is done by the wonderful Beautiful Soup. I wasn’t sure how to “install” it initially (this was my first time using Python), but it was very easy: just have BeautifulSoup.py in the same directory as the script. The other files in the Beautiful Soup zip are unnecessary for our purposes.
Twitter is kind enough to put all the tweets in spans with class “entry-content” so it was quite easy to grab them all with the Beautiful Soup command “findAll”. Interestingly, the first way I tried to search, findAll(’span’,class=”entry-content”), while valid Beautiful Soup syntax, failed because of the word “class.” Fortunately Beautiful Soup provides an alternate syntax which did work. The tweets are ordered chronologically so the newest one has index 0 in the resulting array of spans.
14 # Have we seen that Tweet before, and saved it? 15 newTweet = False 16 filename = os.path.join(os.path.dirname(__file__), 'recentest.txt') 17 try: 18 f = open(filename, 'r') 19 fileTweet = f.read(); 20 if fileTweet != twitterTweet: 21 newTweet = True 22 f.close() 23 except IOError: 24 newTweet = True # file not present, make new file with current tweet
The script keeps a text file “recentest.txt” (as in, “most recent”…) with the most recent tweet it knows about. At first I opened the file with —open(’recentest.txt’, ‘r’)—, but relative paths in Python seem to be relative to the directory from which the script is executed rather than where it resides. So when cron executed the script, it left a “recentest.txt” file in my home directory rather than in the script’s directory where I wanted it. Using the —os.path.dirname— construction fixed it.
I close the file so I can reopen it later with ‘w’ if there’s a new tweet. Opening a file with ‘w’ in Python overwrites it. This way the file always contains just the most recent tweet.
31 # prepare mail messages: 32 mailMsg = "to:name@example.com\n" 33 mailMsg += "subject:Update from cupcakory\n\n" 34 mailMsg += twitterTweet 35 mailMsg += "\n"
The script sends an email by piping some text to sendmail -t . The -t option means the text is in a particular format: the first few lines include to, cc, bcc, and/or subject, then there’s a blank line, and then the body of the message.
42 # send via sendmail subprocess 43 p = sub.Popen(['/usr/sbin/sendmail -t'], shell=True, stdin=sub.PIPE) 44 p.communicate(input=mailMsg)
These lines are akin to:
~$ echo "[the mail message here]" | sendmail -t
The pipe is implemented with the Python subprocess.Popen module, which apparently replaces the deprecated os.popen method. You’re supposed to pass in the shell command and options as a list, ['/usr/sbin/sendmail','-t'], but I couldn’t get it to work, so I just made it all one string.
One thing that got me at first was that I needed to use the full path to sendmail in order for it to work from cron. Just ’sendmail’ worked on all my testing, but I guess the cron user doesn’t have my $PATH or something, so it wasn’t able to send the mail.
The last piece of the puzzle to make it all work is cron. I made this script a script by adding #!/usr/bin/python to the top, and chmod’ing it to be executable. Then I had cron execute it hourly. Edit your crontab with crontab -e. To execute the script at the top of every hour, it would be
0 * * * * /home/path/to/script.py
Note that this method means cron will execute the script as you, so be on the lookout in your email from such emails. Mine hit my spam folder at first…
Tue, Sep 14, 2010 | For updates follow me on twitter