voice.py
Go to the documentation of this file.
00001 from conf import config
00002 from util import *
00003 import settings
00004 import os
00005 
00006 if settings.DEBUG:
00007     import logging
00008     logging.basicConfig()
00009     log = logging.getLogger('PyGoogleVoice')
00010     log.setLevel(logging.DEBUG)
00011 else:
00012     log = None
00013 
00014 class Voice(object):
00015     """
00016     Main voice instance for interacting with the Google Voice service
00017     Handles login/logout and most of the baser HTTP methods
00018     """
00019     def __init__(self):
00020         install_opener(build_opener(HTTPCookieProcessor(CookieJar())))
00021 
00022         for name in settings.FEEDS:
00023             setattr(self, name, self.__get_xml_page(name))
00024         
00025     ######################
00026     # Some handy methods
00027     ######################  
00028     def special(self):
00029         """
00030         Returns special identifier for your session (if logged in)
00031         """
00032         if hasattr(self, '_special') and getattr(self, '_special'):
00033             return self._special
00034         try:
00035             try:
00036                 regex = bytes("('_rnr_se':) '(.+)'", 'utf8')
00037             except TypeError:
00038                 regex = bytes("('_rnr_se':) '(.+)'")
00039         except NameError:
00040             regex = r"('_rnr_se':) '(.+)'"
00041         try:
00042             sp = re.search(regex, urlopen(settings.INBOX).read()).group(2)
00043         except AttributeError:
00044             sp = None
00045         self._special = sp
00046         return sp
00047     special = property(special)
00048     
00049     def login(self, email=None, passwd=None):
00050         """
00051         Login to the service using your Google Voice account
00052         Credentials will be propmpted for if not given as args or in the ``~/.gvoice`` config file
00053         """
00054         if hasattr(self, '_special') and getattr(self, '_special'):
00055             return self
00056         
00057         if email is None:
00058             email = config.email
00059         if email is None:
00060             email = input('Email address: ')
00061         
00062         if passwd is None:
00063             passwd = config.password
00064         if passwd is None:
00065             from getpass import getpass
00066             passwd = getpass()
00067 
00068         content = self.__do_page('login').read()
00069         # holy hackjob
00070         galx = re.search(r"name=\"GALX\"\s+value=\"(.+)\"", content).group(1)
00071         self.__do_page('login', {'Email': email, 'Passwd': passwd, 'GALX': galx})
00072         
00073         del email, passwd
00074         
00075         try:
00076             assert self.special
00077         except (AssertionError, AttributeError):
00078             raise LoginError
00079 
00080         return self
00081         
00082     def logout(self):
00083         """
00084         Logs out an instance and makes sure it does not still have a session
00085         """
00086         self.__do_page('logout')
00087         del self._special 
00088         assert self.special == None
00089         return self
00090         
00091     def call(self, outgoingNumber, forwardingNumber=None, phoneType=None, subscriberNumber=None):
00092         """
00093         Make a call to an ``outgoingNumber`` from your ``forwardingNumber`` (optional).
00094         If you pass in your ``forwardingNumber``, please also pass in the correct ``phoneType``
00095         """        
00096         if forwardingNumber is None:
00097             forwardingNumber = config.forwardingNumber
00098         if phoneType is None:
00099             phoneType = config.phoneType
00100             
00101         self.__validate_special_page('call', {
00102             'outgoingNumber': outgoingNumber,
00103             'forwardingNumber': forwardingNumber,
00104             'subscriberNumber': subscriberNumber or 'undefined',
00105             'phoneType': phoneType,
00106             'remember': '1'
00107         })
00108         
00109     __call__ = call
00110     
00111     def cancel(self, outgoingNumber=None, forwardingNumber=None):
00112         """
00113         Cancels a call matching outgoing and forwarding numbers (if given). 
00114         Will raise an error if no matching call is being placed
00115         """
00116         self.__validate_special_page('cancel', {
00117             'outgoingNumber': outgoingNumber or 'undefined',
00118             'forwardingNumber': forwardingNumber or 'undefined',
00119             'cancelType': 'C2C',
00120         })
00121 
00122     def phones(self):
00123         """
00124         Returns a list of ``Phone`` instances attached to your account.
00125         """
00126         return [Phone(self, data) for data in self.contacts['phones'].values()]
00127     phones = property(phones)
00128 
00129     def settings(self):
00130         """
00131         Dict of current Google Voice settings
00132         """
00133         return AttrDict(self.contacts['settings'])
00134     settings = property(settings)
00135     
00136     def send_sms(self, phoneNumber, text):
00137         """
00138         Send an SMS message to a given ``phoneNumber`` with the given ``text`` message
00139         """
00140         self.__validate_special_page('sms', {'phoneNumber': phoneNumber, 'text': text})
00141 
00142     def search(self, query):
00143         """
00144         Search your Google Voice Account history for calls, voicemails, and sms
00145         Returns ``Folder`` instance containting matching messages
00146         """
00147         return self.__get_xml_page('search', data='?q=%s' % quote(query))()
00148         
00149     def download(self, msg, adir=None):
00150         """
00151         Download a voicemail or recorded call MP3 matching the given ``msg``
00152         which can either be a ``Message`` instance, or a SHA1 identifier. 
00153         Saves files to ``adir`` (defaults to current directory). 
00154         Message hashes can be found in ``self.voicemail().messages`` for example. 
00155         Returns location of saved file.
00156         """
00157         from os import path,getcwd
00158         if isinstance(msg, Message):
00159             msg = msg.id
00160         assert is_sha1(msg), 'Message id not a SHA1 hash'
00161         if adir is None:
00162             adir = getcwd()
00163         try:
00164             response = self.__do_page('download', msg)
00165         except:
00166             raise DownloadError
00167         fn = path.join(adir, '%s.mp3' % msg)
00168         fo = open(fn, 'wb')
00169         fo.write(response.read())
00170         fo.close()
00171         return fn
00172     
00173     def contacts(self):
00174         """
00175         Partial data of your Google Account Contacts related to your Voice account.
00176         For a more comprehensive suite of APIs, check out http://code.google.com/apis/contacts/docs/1.0/developers_guide_python.html
00177         """
00178         if hasattr(self, '_contacts'):
00179             return self._contacts
00180         self._contacts = self.__get_xml_page('contacts')()
00181         return self._contacts
00182     contacts = property(contacts)
00183 
00184     ######################
00185     # Helper methods
00186     ######################
00187 
00188     
00189     def __do_page(self, page, data=None, headers={}):
00190         """
00191         Loads a page out of the settings and pass it on to urllib Request
00192         """
00193         page = page.upper()
00194         if isinstance(data, dict) or isinstance(data, tuple):
00195             data = urlencode(data)
00196         headers.update({'User-Agent': 'PyGoogleVoice/0.5'})
00197         if log:
00198             log.debug('%s?%s - %s' % (getattr(settings, page)[22:], data or '', headers))
00199         if page in ('DOWNLOAD','XML_SEARCH'):
00200             return urlopen(Request(getattr(settings, page) + data, None, headers))
00201         if data:
00202             headers.update({'Content-type': 'application/x-www-form-urlencoded;charset=utf-8'})
00203         return urlopen(Request(getattr(settings, page), data, headers))
00204 
00205     def __validate_special_page(self, page, data={}, **kwargs):
00206         """
00207         Validates a given special page for an 'ok' response
00208         """
00209         data.update(kwargs)
00210         load_and_validate(self.__do_special_page(page, data))
00211 
00212     _Phone__validate_special_page = __validate_special_page
00213     
00214     def __do_special_page(self, page, data=None, headers={}):
00215         """
00216         Add self.special to request data
00217         """
00218         assert self.special, 'You must login before using this page'
00219         if isinstance(data, tuple):
00220             data += ('_rnr_se', self.special)
00221         elif isinstance(data, dict):
00222             data.update({'_rnr_se': self.special})
00223         return self.__do_page(page, data, headers)
00224         
00225     _Phone__do_special_page = __do_special_page
00226     
00227     def __get_xml_page(self, page, data=None, headers={}):
00228         """
00229         Return XMLParser instance generated from given page
00230         """
00231         return XMLParser(self, page, lambda: self.__do_special_page('XML_%s' % page.upper(), data, headers).read())
00232       
00233     def __messages_post(self, page, *msgs, **kwargs):
00234         """
00235         Performs message operations, eg deleting,staring,moving
00236         """
00237         data = kwargs.items()
00238         for msg in msgs:
00239             if isinstance(msg, Message):
00240                 msg = msg.id
00241             assert is_sha1(msg), 'Message id not a SHA1 hash'
00242             data += (('messages',msg),)
00243         return self.__do_special_page(page, dict(data))
00244     
00245     _Message__messages_post = __messages_post


continuous_ops_alerts
Author(s): Eitan Marder-Eppstein
autogenerated on Fri Dec 6 2013 19:59:13