Showing posts with label regular expressions. Show all posts
Showing posts with label regular expressions. Show all posts

Tuesday, July 21, 2009

Redirect Python output

EDIT: Updated scripts to use the new login system described here.

Today at work I was working on a script to create some reports for one of my superiors. During my testing I was outputting everything to the screen so I could view it quickly and easily. My boss needed me to send him a file containing the data I was generating, and I didn't look forward to changing all of my print statements (
my script had many, many print statements) to something like "file.write(whateverwasbeingprintedbefore)". I could have done this, of course (and it could have been done in one easy step with the help of RegexBuddy) but that still seemed too inflexible of a solution. Python has a very simple solution for this problem, and it just takes an extra two lines of code.

My work example would have been boring here, so I thought up something else useful. Using my Google Voice Login class, I created another script to print out whatever is in my SMS inbox (up to the first 10) in a semi decent format. I used a couple of regular expressions, which I could not have created without RegexBuddy, to parse the page for the information I wanted. Here is that script (which requires the gvoice.py module I wrote):


# Print SMS inbox
from gvoice import GoogleVoiceLogin
import urllib
import re
import getpass

# Create an instance of a GoogleVoiceLogin object
# This will prompt you for your Google Account credentials
email = raw_input('Enter your Google Username: ')
password = getpass.getpass('Enter your password: ')

gv_login = GoogleVoiceLogin(email, password)

# Create our regular expressions (Created in RegexBuddy)
# Regular Expression to gather information on each SMS Conversation
sms_conversation_regex = re.compile(
r"""<span\sclass="gc-message-name">\s* # Get the message conversation info div
<span\sclass="">([^<]*?)</span>\s* # Get who originated the conversation
<span.*?</span>.*? # Eat this div - we don't need it
.*?>([^<]*).*? # Get who the conversation was started with
<div\sclass="gc-message-message-display">\s*
(<div\sclass="gc-message-sms-row">.*? # Get all the messages of the conversation
</div>\s*)* # Close up our divs
</div>""",
re.DOTALL | re.VERBOSE)

# Regular expression to gather information on individual messages within an SMS conversaion
sms_details_regex = re.compile(
r"""<div\sclass="gc-message-sms-row">.*? # Get up the message div
<span\sclass="gc-message-sms-from">\s*(.*?)\s* # Get who the message was from
</span>.*?
<span\sclass="gc-message-sms-text">\s*([^<]*)\s*.*?</div> # Get the message data""",
re.DOTALL | re.VERBOSE)

# Get the open (Cookie data still intact)
opener =gv_login.opener

sms_inbox_content = opener.open("https://www.google.com/voice/inbox/recent/sms/").read()

# Nested for-loops of regular expressions - not the best way, but the easiest for now.
for sms_conversation_match in sms_conversation_regex.finditer(sms_inbox_content):
print
print "---{0} to {1} {2}".format(sms_conversation_match.group(1), sms_conversation_match.group(2), '-' * 50)
for message_details_match in sms_details_regex.finditer(sms_conversation_match.group()):
print "{0}: {1}".format(message_details_match.group(1), message_details_match.group(2))


Running this script will show you the first 10 SMS conversations in your inbox, if there are any. But, it is limited right now to displaying the information on the screen. Using these next two lines, you can put all of this information in a file of your choice:


import sys

sys.stdout = open("SMS Conversations.txt", "w")



To get this to work in the SMS inbox script, I insert the "import sys" command right below the other imports. The "sys.stdout" line goes right below the line where I instantiate the GoogleVoiceLogin object. I deliberately do that, otherwise I won't be able to see the prompt, because it will be written to the file!

This is a very easy way to change the output of your program - I hope you find it as useful as I did.

Here is the final script:

# Print SMS inbox
from gvoice import GoogleVoiceLogin
import urllib
import re
import sys
import getpass

# Create an instance of a GoogleVoiceLogin object
# This will prompt you for your Google Account credentials
email = raw_input('Enter your Google Username: ')
password = getpass.getpass('Enter your password: ')

gv_login = GoogleVoiceLogin(email, password)

sys.stdout = open("SMS Conversations.txt", "w")

# Create our regular expressions (Created in RegexBuddy)
# Regular Expression to gather information on each SMS Conversation
sms_conversation_regex = re.compile(
r"""<span\sclass="gc-message-name">\s* # Get the message conversation info div
<span\sclass="">([^<]*?)</span>\s* # Get who originated the conversation
<span.*?</span>.*? # Eat this div - we don't need it
.*?>([^<]*).*? # Get who the conversation was started with
<div\sclass="gc-message-message-display">\s*
(<div\sclass="gc-message-sms-row">.*? # Get all the messages of the conversation
</div>\s*)* # Close up our divs
</div>""",
re.DOTALL | re.VERBOSE)

# Regular expression to gather information on individual messages within an SMS conversaion
sms_details_regex = re.compile(
r"""<div\sclass="gc-message-sms-row">.*? # Get up the message div
<span\sclass="gc-message-sms-from">\s*(.*?)\s* # Get who the message was from
</span>.*?
<span\sclass="gc-message-sms-text">\s*([^<]*)\s*.*?</div> # Get the message data""",
re.DOTALL | re.VERBOSE)

# Get the open (Cookie data still intact)
opener =gv_login.opener

sms_inbox_content = opener.open("https://www.google.com/voice/inbox/recent/sms/").read()

# Nested for-loops of regular expressions - not the best way, but the easiest for now.
for sms_conversation_match in sms_conversation_regex.finditer(sms_inbox_content):
print
print "---{0} to {1} {2}".format(sms_conversation_match.group(1), sms_conversation_match.group(2), '-' * 50)
for message_details_match in sms_details_regex.finditer(sms_conversation_match.group()):
print "{0}: {1}".format(message_details_match.group(1), message_details_match.group(2))
print "---{0} to {1} {2}".format(sms_conversation_match.group(1), sms_conversation_match.group(2), '-' * 50)
for message_details_match in sms_details_regex.finditer(sms_conversation_match.group()):
print "{0}: {1}".format(message_details_match.group(1), message_details_match.group(2))

Monday, July 13, 2009

Google Voice - Python - SMS

EDIT: Google recently made some behind the scenes changes to the login page, which broke this script. Please see the new and improved script here.

After a long wait, I finally received my Google Voice invitation last weekend. It offers a lot of features, but one that I was really excited about was the free SMS service. I don't have a cell phone, but nearly all my family members, friends and co-workers do. The majority of them are more prone to check their phone than their email, so sending a text is the best way to contact them. For a while, I used Gmail's SMS Lab to get in contact with the leaders and the Scouts of a Boy Scout troop that I participate in, but Gmail shut that down recently.

After I got my account, I began thinking about how neat it would be to be able to set up a script to send reminder text messages for me (preferably scheduled) using my account. I searched all over the internet to find out if there was an API provided by Google to do this -like their excellent gdata API - but no luck. I didn't give up there, there had to be a way.

A while ago, I learned that you can use HTTPCookieProcessor() from the urllib2 module to create an opener that will keep track of Cookie data sent back and forth from a server. So, with the help of Firebug (the best friend of many web-developers) I looked at the headers and POST data being sent when logging in to Google Voice, and when sending a text message. Turns out, it isn't very hard to log into your account, grab some necessary information from the Google Voice Home page (the inbox) and send a text.

This script will do just that one thing - log in your account, load the homepage to get a hidden form field's value, and then send one text message to one number. It will still show up in your outbox, so you aren't losing anything by using this method. This ability opens up a lot of possibilities, especially when coupled with the Gdata Contacts API. I have a few little scripts that I will be creating to send out weekly reminders to other people - and might use Google App Engine to make it even more useful and automated.


# Get URL handling support
import urllib2, urllib
# Get regular expression support
import re

# Google Account login credentials
email = 'YOUR_EMAIL'
password = 'YOUR_PASSWORD'

# Text message details
sendToNumber = 'NUMBER_TO_SEND_A_TEXT_TO'
messageToSend = 'I used Python to send this!'

# Set up an opener with HTTPCookieProcessor
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
urllib2.install_opener(opener)

# Set up login credentials for Google Accounts
# The 'continue' param redirects us to the Google Voice
# homepage, and gives us necessary cookie info
loginParams = urllib.urlencode( {
'Email' : email,
'Passwd' : password,
'continue' : 'https://www.google.com/voice/account/signin',
} )

# Perform the login. Cookie info sent back will be saved, so we remain logged in
# for future requests when using the opener
opener.open( 'https://www.google.com/accounts/ServiceLoginAuth', loginParams)

# Need to load the homepage to find user specific data
googleVoiceHomeData = opener.open('https://www.google.com/voice/#inbox').read()

# Go through the home page and grab the value for the hidden
# form field "_rnr_se", which must be included when sending texts
match = re.search('name="_rnr_se".*?value="(.*?)"', googleVoiceHomeData)
_rnr_se = match.group(1)

# Set up parameters for sending text
sendTextParams = urllib.urlencode({
'_rnr_se': _rnr_se,
'phoneNumber': sendToNumber,
'text': messageToSend
})

# Send the text, store the return value
f = opener.open('https://www.google.com/voice/sms/send/', sendTextParams)
data = f.read()
print data
f.close()


Right now, if it succeeds nothing spectacular happens - it simply prints out the response: {"ok":true,"data":{"code":0}}. If it fails, {"ok":false,"data":{"code":20}}. Just look at the "ok" value to see if it worked.

You may notice that I don't have much (any) error checking in the script, nor do I usually when putting together scripts quickly just to test an idea out. When I do create something actually useful using this above script, I will put in the appropriate "checker" code. Just be aware that a text message can be 160 characters long, if you go over the remaining characters will be sent in a separate message.

Have fun, and don't be evil.