Work in progress on Google Voice and Google Data
authorepage <eopage@byu.net>
Fri, 13 Mar 2009 02:34:53 +0000 (02:34 +0000)
committerepage <eopage@byu.net>
Fri, 13 Mar 2009 02:34:53 +0000 (02:34 +0000)
git-svn-id: file:///svnroot/gc-dialer/trunk@215 c39d3808-3fe2-4d86-a59f-b7f623ee9f21

Makefile
src/gdata_backend.py [new file with mode: 0644]
src/gmail_backend.py
src/gv_backend.py

index 9069a40..56869db 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,7 @@ SOURCE=$(SOURCE_PATH)/dialer.py \
           $(SOURCE_PATH)/null_views.py \
           $(SOURCE_PATH)/file_backend.py \
           $(SOURCE_PATH)/evo_backend.py \
+          $(SOURCE_PATH)/gdata_backend.py \
           $(SOURCE_PATH)/gv_backend.py \
           $(SOURCE_PATH)/gc_backend.py \
           $(SOURCE_PATH)/browser_emu.py \
diff --git a/src/gdata_backend.py b/src/gdata_backend.py
new file mode 100644 (file)
index 0000000..a1b3ca4
--- /dev/null
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+
+import sys
+sys.path.insert(0,"../gdata/src/")
+
+
+try:
+       import atom
+       import gdata
+       gdata.contacts
+except (ImportError, AttributeError):
+       atom = None
+       gdata = None
+
+
+class GDataAddressBook(object):
+       """
+       """
+
+       def __init__(self, client, id = None):
+               self._client = client
+               self._id = id
+               self._contacts = []
+
+       def clear_caches(self):
+               del self._contacts[:]
+
+       @staticmethod
+       def factory_name():
+               return "GData"
+
+       @staticmethod
+       def contact_source_short_name(contactId):
+               return "gd"
+
+       def get_contacts(self):
+               """
+               @returns Iterable of (contact id, contact name)
+               """
+               return []
+
+       def get_contact_details(self, contactId):
+               """
+               @returns Iterable of (Phone Type, Phone Number)
+               """
+               return []
+
+       def _get_contacts(self):
+               if len(self._contacts) != 0:
+                       return self._contacts
+               feed = self._get_feed()
+               for entry in feed:
+                       name = entry.title.text
+                       print name
+                       for extendedProperty in entry.extended_property:
+                               if extendedProperty.value:
+                                       print extendedProperty.value
+                               else:
+                                       print extendedProperty.GetXmlBlobString()
+
+       def _get_feed(self):
+               if self._id is None:
+                       return self._client.GetContactsFeed()
+               else:
+                       pass
+
+
+class GDataAddressBookFactory(object):
+
+       def __init__(self, username, password):
+               self._username = username
+               self._password = password
+               self._client = None
+
+               if gdata is None:
+                       return
+               self._client = gdata.contacts.service.ContactsService()
+               self._client.email = username
+               self._client.password = password
+               self._client.source = "DialCentra"
+               self._client.ProgrammaticLogin()
+
+       def clear_caches(self):
+               if gdata is None:
+                       return
+               pass
+
+       def get_addressbooks(self):
+               """
+               @returns Iterable of (Address Book Factory, Book Id, Book Name)
+               """
+               if gdata is None:
+                       return
+               feed = self._client.GetGroupsFeed()
+               for entry in feed:
+                       id = entry.id.text
+                       name = entry.title.text
+                       yield self, id, name
+               yield self, "all", "All"
+
+       def open_addressbook(self, bookId):
+               if gdata is None:
+                       return
+               if bookId == "all":
+                       return GDataAddressBook(self._client)
+               else:
+                       return GDataAddressBook(self._client, bookId)
+
+       @staticmethod
+       def factory_name():
+               return "GData"
+
+
+def print_gbooks(username, password):
+       """
+       Included here for debugging.
+
+       Either insert it into the code or launch python with the "-i" flag
+       """
+       abf = GDataAddressBookFactory(username, password)
+       for book in abf.get_addressbooks():
+               ab = abf.open_addressbook(book[1])
+               print book
+               for contact in ab.get_contacts():
+                       print "\t", contact
+                       for details in ab.get_contact_details(contact[0]):
+                               print "\t\t", details
index d80ae7f..bead963 100644 (file)
@@ -45,7 +45,7 @@ class GMailAddressBook(object):
 
                self._account = libgmail.GmailAccount(username, password)
                self._gmailContacts = self._account.getContacts()
-       
+
        @classmethod
        def is_supported(cls):
                return libgmail is not None
@@ -58,7 +58,7 @@ class GMailAddressBook(object):
                        return
 
                yield self, "", ""
-       
+
        def open_addressbook(self, bookId):
                return self
 
@@ -77,7 +77,7 @@ class GMailAddressBook(object):
                if not self.is_supported():
                        return
                pass
-       
+
        def get_contact_details(self, contactId):
                """
                @returns Iterable of (Phone Type, Phone Number)
index 227838b..35163a8 100644 (file)
@@ -37,13 +37,13 @@ import socket
 socket.setdefaulttimeout(5)
 
 
-class GCDialer(object):
+class GVDialer(object):
        """
        This class encapsulates all of the knowledge necessary to interace with the grandcentral servers
        the functions include login, setting up a callback number, and initalting a callback
        """
 
-       _gcDialingStrRe = re.compile("This may take a few seconds", re.M)
+       _gvDialingStrRe = re.compile("This may take a few seconds", re.M)
        _accessTokenRe = re.compile(r"""<input type="hidden" name="a_t" [^>]*value="(.*)"/>""")
        _isLoginPageRe = re.compile(r"""<form method="post" action="https://www.grandcentral.com/mobile/account/login">""")
        _callbackRe = re.compile(r"""name="default_number" value="(\d+)" />\s+(.*)\s$""", re.M)
@@ -56,19 +56,19 @@ class GCDialer(object):
 
        _validateRe = re.compile("^[0-9]{10,}$")
 
-       _forwardselectURL = "http://www.grandcentral.com/mobile/settings/forwarding_select"
-       _loginURL = "https://www.grandcentral.com/mobile/account/login"
-       _setforwardURL = "http://www.grandcentral.com/mobile/settings/set_forwarding?from=settings"
-       _clicktocallURL = "http://www.grandcentral.com/mobile/calls/click_to_call?a_t=%s&destno=%s"
-       _inboxallURL = "http://www.grandcentral.com/mobile/messages/inbox?types=all"
-       _contactsURL = "http://www.grandcentral.com/mobile/contacts"
-       _contactDetailURL = "http://www.grandcentral.com/mobile/contacts/detail"
+       _forwardselectURL = "http://www.google.com/voice/m/settings/forwarding_select"
+       _loginURL = "https://www.google.com/voice/m/account/login"
+       _setforwardURL = "http://www.google.com/voice/m/settings/set_forwarding?from=settings"
+       _clicktocallURL = "http://www.google.com/voice/m/caller?number=%s"
+       _inboxallURL = "http://www.google.com/voice/m/i"
+       _contactsURL = "http://www.google.com/voice/m/contacts"
+       _contactDetailURL = "http://www.google.com/voice/m/contact"
 
        def __init__(self, cookieFile = None):
                # Important items in this function are the setup of the browser emulation and cookie file
                self._browser = MozillaEmulator(None, 0)
                if cookieFile is None:
-                       cookieFile = os.path.join(os.path.expanduser("~"), ".gc_cookies.txt")
+                       cookieFile = os.path.join(os.path.expanduser("~"), ".gv_cookies.txt")
                self._browser.cookies.filename = cookieFile
                if os.path.isfile(cookieFile):
                        self._browser.cookies.load()
@@ -91,12 +91,12 @@ class GCDialer(object):
                        return True
 
                try:
-                       forwardSelectionPage = self._browser.download(GCDialer._forwardselectURL)
+                       forwardSelectionPage = self._browser.download(self._forwardselectURL)
                except urllib2.URLError, e:
-                       raise RuntimeError("%s is not accesible" % GCDialer._clicktocallURL)
+                       raise RuntimeError("%s is not accesible" % self._clicktocallURL)
 
                self._browser.cookies.save()
-               if GCDialer._isLoginPageRe.search(forwardSelectionPage) is None:
+               if self._isLoginPageRe.search(forwardSelectionPage) is None:
                        self._grab_token(forwardSelectionPage)
                        self._lastAuthed = time.time()
                        return True
@@ -114,9 +114,9 @@ class GCDialer(object):
                loginPostData = urllib.urlencode( {'username' : username , 'password' : password } )
 
                try:
-                       loginSuccessOrFailurePage = self._browser.download(GCDialer._loginURL, loginPostData)
+                       loginSuccessOrFailurePage = self._browser.download(self._loginURL, loginPostData)
                except urllib2.URLError, e:
-                       raise RuntimeError("%s is not accesible" % GCDialer._clicktocallURL)
+                       raise RuntimeError("%s is not accesible" % self._clicktocallURL)
 
                return self.is_authed()
 
@@ -145,14 +145,14 @@ class GCDialer(object):
 
                try:
                        callSuccessPage = self._browser.download(
-                               GCDialer._clicktocallURL % (self._accessToken, number),
+                               self._clicktocallURL % (number, ),
                                None,
                                {'Referer' : 'http://www.grandcentral.com/mobile/messages'}
                        )
                except urllib2.URLError, e:
-                       raise RuntimeError("%s is not accesible" % GCDialer._clicktocallURL)
+                       raise RuntimeError("%s is not accesible" % self._clicktocallURL)
 
-               if GCDialer._gcDialingStrRe.search(callSuccessPage) is None:
+               if self._gvDialingStrRe.search(callSuccessPage) is None:
                        raise RuntimeError("Grand Central returned an error")
 
                return True
@@ -221,9 +221,9 @@ class GCDialer(object):
                        'default_number': callbacknumber
                })
                try:
-                       callbackSetPage = self._browser.download(GCDialer._setforwardURL, callbackPostData)
+                       callbackSetPage = self._browser.download(self._setforwardURL, callbackPostData)
                except urllib2.URLError, e:
-                       raise RuntimeError("%s is not accesible" % GCDialer._clicktocallURL)
+                       raise RuntimeError("%s is not accesible" % self._clicktocallURL)
 
                self._browser.cookies.save()
                return True
@@ -242,9 +242,9 @@ class GCDialer(object):
                @returns Iterable of (personsName, phoneNumber, date, action)
                """
                try:
-                       recentCallsPage = self._browser.download(GCDialer._inboxallURL)
+                       recentCallsPage = self._browser.download(self._inboxallURL)
                except urllib2.URLError, e:
-                       raise RuntimeError("%s is not accesible" % GCDialer._clicktocallURL)
+                       raise RuntimeError("%s is not accesible" % self._clicktocallURL)
 
                for match in self._inboxRe.finditer(recentCallsPage):
                        phoneNumber = match.group(4)
@@ -264,11 +264,11 @@ class GCDialer(object):
 
        @staticmethod
        def contact_source_short_name(contactId):
-               return "GC"
+               return "GV"
 
        @staticmethod
        def factory_name():
-               return "Grand Central"
+               return "Google Voice"
 
        def get_contacts(self):
                """
@@ -277,12 +277,12 @@ class GCDialer(object):
                if self.__contacts is None:
                        self.__contacts = []
 
-                       contactsPagesUrls = [GCDialer._contactsURL]
+                       contactsPagesUrls = [self._contactsURL]
                        for contactsPageUrl in contactsPagesUrls:
                                try:
                                        contactsPage = self._browser.download(contactsPageUrl)
                                except urllib2.URLError, e:
-                                       raise RuntimeError("%s is not accesible" % GCDialer._clicktocallURL)
+                                       raise RuntimeError("%s is not accesible" % self._clicktocallURL)
                                for contact_match in self._contactsRe.finditer(contactsPage):
                                        contactId = contact_match.group(1)
                                        contactName = contact_match.group(2)
@@ -303,9 +303,9 @@ class GCDialer(object):
                @returns Iterable of (Phone Type, Phone Number)
                """
                try:
-                       detailPage = self._browser.download(GCDialer._contactDetailURL + '/' + contactId)
+                       detailPage = self._browser.download(self._contactDetailURL + '/' + contactId)
                except urllib2.URLError, e:
-                       raise RuntimeError("%s is not accesible" % GCDialer._clicktocallURL)
+                       raise RuntimeError("%s is not accesible" % self._clicktocallURL)
 
                for detail_match in self._contactDetailPhoneRe.finditer(detailPage):
                        phoneType = detail_match.group(1)
@@ -314,12 +314,22 @@ class GCDialer(object):
 
        def _grab_token(self, data):
                "Pull the magic cookie from the datastream"
-               atGroup = GCDialer._accessTokenRe.search(data)
+               atGroup = self._accessTokenRe.search(data)
                self._accessToken = atGroup.group(1)
 
-               anGroup = GCDialer._accountNumRe.search(data)
+               anGroup = self._accountNumRe.search(data)
                self._accountNum = anGroup.group(1)
 
                self._callbackNumbers = {}
-               for match in GCDialer._callbackRe.finditer(data):
+               for match in self._callbackRe.finditer(data):
                        self._callbackNumbers[match.group(1)] = match.group(2)
+
+
+def test_backend(username, password):
+       backend = GVDialer()
+       print "Authenticated: ", backend.is_authed()
+       backend.login(username, password)
+       print "Authenticated: ", backend.is_authed()
+       print "Account: ", backend.get_account_number()
+       print "Callback: ", backend.get_callback_numbers()
+       print "Recent: ", backend.get_recent()