#!/usr/bin/python
-# GC Dialer - Front end for Google's Grand Central service.
+# DialCentral - Front end for Google's Grand Central service.
# Copyright (C) 2008 Eric Warnke ericew AT gmail DOT com
#
# This library is free software; you can redistribute it and/or
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""
-Grandcentral Dialer backend code
+Grandcentral backend code
"""
from browser_emu import MozillaEmulator
+import socket
+
+
+socket.setdefaulttimeout(5)
+
class GCDialer(object):
"""
_inboxRe = re.compile(r"""<td>.*?(voicemail|received|missed|call return).*?</td>\s+<td>\s+<font size="2">\s+(.*?)\s+ \| \s+<a href="/mobile/contacts/.*?">(.*?)\s?</a>\s+<br/>\s+(.*?)\s?<a href=""", re.S)
_contactsRe = re.compile(r"""<a href="/mobile/contacts/detail/(\d+)">(.*?)</a>""", re.S)
_contactsNextRe = re.compile(r""".*<a href="/mobile/contacts(\?page=\d+)">Next</a>""", re.S)
- _contactDetailGroupRe = re.compile(r"""Group:\s*(\w*)""", re.S)
- _contactDetailPhoneRe = re.compile(r"""(\w+):[0-9\-\(\) \t]*?<a href="/mobile/calls/click_to_call\?destno=(\d+).*?">call</a>""", re.S)
+ _contactDetailGroupRe = re.compile(r"""Group:\s*(\w*)""", re.S)
+ _contactDetailPhoneRe = re.compile(r"""(\w+):[0-9\-\(\) \t]*?<a href="/mobile/calls/click_to_call\?destno=(\d+).*?">call</a>""", re.S)
_validateRe = re.compile("^[0-9]{10,}$")
def __init__(self, cookieFile = None):
# Important items in this function are the setup of the browser emulation and cookie file
- self._msg = ""
-
self._browser = MozillaEmulator(None, 0)
if cookieFile is None:
- cookieFile = os.path.join(os.path.expanduser("~"), ".gc_dialer_cookies.txt")
+ cookieFile = os.path.join(os.path.expanduser("~"), ".gc_cookies.txt")
self._browser.cookies.filename = cookieFile
if os.path.isfile(cookieFile):
self._browser.cookies.load()
self._callbackNumbers = {}
self._lastAuthed = 0.0
+ self.__contacts = None
+
def is_authed(self, force = False):
"""
Attempts to detect a current session and pull the auth token ( a_t ) from the page.
@returns If authenticated
"""
- if time.time() - self._lastAuthed < 60 and not force:
+ if (time.time() - self._lastAuthed) < 60 and not force:
return True
try:
self._browser.cookies.clear()
self._browser.cookies.save()
+ self.clear_caches()
+
def dial(self, number):
"""
This is the main function responsible for initating the callback
"""
- self._msg = ""
-
# If the number is not valid throw exception
if not self.is_valid_syntax(number):
raise ValueError('number is not valid')
# No point if we don't have the magic cookie
if not self.is_authed():
- self._msg = "Not authenticated"
- return False
+ raise RuntimeError("Not Authenticated")
# Strip leading 1 from 11 digit dialing
if len(number) == 11 and number[0] == 1:
)
except urllib2.URLError, e:
warnings.warn("%s is not accesible" % GCDialer._clicktocallURL, UserWarning, 2)
- return False
+ raise RuntimeError("%s is not accesible" % GCDialer._clicktocallURL)
- if GCDialer._gcDialingStrRe.search(callSuccessPage) is not None:
- return True
- else:
- self._msg = "Grand Central returned an error"
- return False
+ if GCDialer._gcDialingStrRe.search(callSuccessPage) is None:
+ raise RuntimeError("Grand Central returned an error")
- self._msg = "Unknown Error"
- return False
+ return True
def clear_caches(self):
- pass
+ self.__contacts = None
def is_valid_syntax(self, number):
"""
"""
@returns Iterable of (Address Book Factory, Book Id, Book Name)
"""
- yield self, None, "Grand Central"
-
+ yield self, "", ""
+
def open_addressbook(self, bookId):
return self
+ @staticmethod
+ def contact_source_short_name(contactId):
+ return "GC"
+
+ @staticmethod
+ def factory_name():
+ return "Grand Central"
+
def get_contacts(self):
"""
@returns Iterable of (contact id, contact name)
"""
- contactsPagesUrls = [GCDialer._contactsURL]
- for contactsPageUrl in contactsPagesUrls:
- contactsPage = self._browser.download(contactsPageUrl)
- for contact_match in self._contactsRe.finditer(contactsPage):
- contactId = contact_match.group(1)
- contactName = contact_match.group(2)
- yield contactId, contactName
-
- next_match = self._contactsNextRe.match(contactsPage)
- if next_match is not None:
- newContactsPageUrl = self._contactsURL + next_match.group(1)
- contactsPagesUrls.append(newContactsPageUrl)
-
+ if self.__contacts is None:
+ self.__contacts = []
+
+ contactsPagesUrls = [GCDialer._contactsURL]
+ for contactsPageUrl in contactsPagesUrls:
+ contactsPage = self._browser.download(contactsPageUrl)
+ for contact_match in self._contactsRe.finditer(contactsPage):
+ contactId = contact_match.group(1)
+ contactName = contact_match.group(2)
+ contact = contactId, contactName
+ self.__contacts.append(contact)
+ yield contact
+
+ next_match = self._contactsNextRe.match(contactsPage)
+ if next_match is not None:
+ newContactsPageUrl = self._contactsURL + next_match.group(1)
+ contactsPagesUrls.append(newContactsPageUrl)
+ else:
+ for contact in self.__contacts:
+ yield contact
+
def get_contact_details(self, contactId):
"""
@returns Iterable of (Phone Type, Phone Number)