our lab “owns” an island on the LindenLab grid and we’ve created a “lab” avatar who is the estate owner. as the island is paid for by the lab, we need to have monthly account statements on file for the bean counters. so far, i had to remember to log in to the lab avatar’s account page, copy the account statement and mail it our friendly lady in accounting taking care of these things (yes, we are a small lab)…
…the only problem with that is that i tend to forget these things and our friendly accounting lady has to be rather patient and usually ends up sending reminder notes.
so, after another one of these notes and as it was becoming rather embarrassing that i kept forgetting, i decided to tackle it once and for all and write a python script to do this automatically. the very excellent mechanize python package turned out to be an invaluable tool.
here’s the script in all its glory:
[python]
#!/usr/bin/python
# -*- encoding: utf-8 -*-
import re
import smtplib
import time
from email.mime.text import MIMEText
from mechanize import Browser
from optparse import OptionParser
from ConfigParser import ConfigParser
class Ooops(Exception):
def __init__(self, msg):
self.value = msg
def __str__(self):
return msg
if __name__ == '__main__':
try:
parser = OptionParser()
parser.add_option('-c', '--config', dest = 'config',
help = 'path to configuration file', metavar = 'CONFIG-FILE')
(options, args) = parser.parse_args()
if not options.config:
parser.error('--config option mandatory')
config = ConfigParser()
config.readfp(open(options.config))
slURL = config.get('lindenlab', 'url')
slAvatarFirstname = config.get('lindenlab', 'firstname')
slAvatarLastname = config.get('lindenlab', 'lastname')
slAvatarPassword = config.get('lindenlab', 'password')
browser = Browser()
# open http://www.secondlife.com/ and select the "resident login" link
browser.open(slURL)
browser.follow_link(text_regex = r'Resident\s*Login', nr = 0)
# fill out the login form
browser.select_form(nr = 0)
browser['form[username]'] = slAvatarFirstname
browser['form[lastname]'] = slAvatarLastname
browser['form[password]'] = slAvatarPassword
browser.submit()
# page title should read: "Second Life | Your Account: WayLate Binder"
rePageTitle = re.compile(r'Second\s+Life\s+\|\s+Your Account:')
match = rePageTitle.match(browser.title())
if not match: raise Ooops('not on account page')
# select account history link and retrieve the statement
statement_page = browser.follow_link(text_regex = r'Account\s+History', nr = 0)
statement = statement_page.get_data()
reURL = re.compile(r'^(?P<host>https?://[^/]+)')
match = reURL.match(browser.geturl())
if not match: raise Ooops('no host URL')
url = match.groups('host')
reHref = re.compile(r'href\s*=\s*"/')
statement = reHref.sub('href="%s/' % url, statement)
browser.close()
msgSubject = config.get('mail', 'subject')
msgSubject = time.strftime(msgSubject)
msgTo = config.get('mail', 'to')
msgFrom = config.get('mail', 'from')
msgCc = config.get('mail', 'cc')
msgEnvTo = msgTo.split(',')
if msgCc:
msgEnvTo += msgCc.split(',')
msg = MIMEText(statement, 'html', 'UTF-8')
msg['Subject'] = msgSubject
msg['To'] = msgTo
msg['Cc'] = msgCc
msg['From'] = msgFrom
smtp = smtplib.SMTP('localhost')
smtp.sendmail(msgFrom, msgEnvTo, msg.as_string())
smtp.close()
except Ooops, e:
print 'oops: failed to retrieve account statement: %s' % e.value
[/python]
it reads the avatar name, password and so forth from a configuration file:
[lindenlab] url = http://www.secondlife.com firstname = Foo lastname = Bar password = secret [mail] subject = %Y-%m LindenLab monthly account statement for Foo Bar to = accounting@foo.bar.com cc = me@foo.bar.com from = me@foo.bar.com
adapt the config file to your taste and invoke as follows:
sl-account-statement --config config.cfg
voila!
