the functions include login, setting up a callback number, and initalting a callback
"""
- _tokenRe = re.compile(r"""<input.*?name="_rnr_se".*?value="(.*?)"\s*/>""")
- _accountNumRe = re.compile(r"""<b class="ms\d">(.{14})</b></div>""")
- _callbackRe = re.compile(r"""\s+(.*?):\s*(.*?)<br\s*/>\s*$""", re.M)
- _validateRe = re.compile("^[0-9]{10,}$")
- _gvDialingStrRe = re.compile("This may take a few seconds", re.M)
-
- _contactsRe = re.compile(r"""<a href="/voice/m/contact/(\d+)">(.*?)</a>""", re.S)
- _contactsNextRe = re.compile(r""".*<a href="/voice/m/contacts(\?p=\d+)">Next.*?</a>""", re.S)
- _contactDetailPhoneRe = re.compile(r"""<div.*?>([0-9\-\(\) \t]+?)<span.*?>\((\w+)\)</span>""", re.S)
-
- _clicktocallURL = "https://www.google.com/voice/m/sendcall"
- _smsURL = "https://www.google.com/voice/m/sendsms"
- _contactsURL = "https://www.google.com/voice/mobile/contacts"
- _contactDetailURL = "https://www.google.com/voice/mobile/contact"
-
- _loginURL = "https://www.google.com/accounts/ServiceLoginAuth"
- _setforwardURL = "https://www.google.com//voice/m/setphone"
- _accountNumberURL = "https://www.google.com/voice/mobile"
- _forwardURL = "https://www.google.com/voice/mobile/phones"
-
- _recentCallsURL = "https://www.google.com/voice/inbox/recent/"
- _placedCallsURL = "https://www.google.com/voice/inbox/recent/placed/"
- _receivedCallsURL = "https://www.google.com/voice/inbox/recent/received/"
- _missedCallsURL = "https://www.google.com/voice/inbox/recent/missed/"
- _voicemailURL = "https://www.google.com/voice/inbox/recent/voicemail/"
- _smsURL = "https://www.google.com/voice/inbox/recent/sms/"
-
- _seperateVoicemailsRegex = re.compile(r"""^\s*<div id="(\w+)"\s* class="gc-message.*?">""", re.MULTILINE | re.DOTALL)
- _exactVoicemailTimeRegex = re.compile(r"""<span class="gc-message-time">(.*?)</span>""", re.MULTILINE)
- _relativeVoicemailTimeRegex = re.compile(r"""<span class="gc-message-relative">(.*?)</span>""", re.MULTILINE)
- _voicemailNumberRegex = re.compile(r"""<input type="hidden" class="gc-text gc-quickcall-ac" value="(.*?)"/>""", re.MULTILINE)
- _prettyVoicemailNumberRegex = re.compile(r"""<span class="gc-message-type">(.*?)</span>""", re.MULTILINE)
- _voicemailLocationRegex = re.compile(r"""<span class="gc-message-location">(.*?)</span>""", re.MULTILINE)
- _voicemailMessageRegex = re.compile(r"""<span class="gc-word-(.*?)">(.*?)</span>""", re.MULTILINE)
-
def __init__(self, cookieFile = None):
# Important items in this function are the setup of the browser emulation and cookie file
self._browser = browser_emu.MozillaEmulator(1)
self._lastAuthed = time.time()
return True
+ _loginURL = "https://www.google.com/accounts/ServiceLoginAuth"
+
def login(self, username, password):
"""
Attempt to login to grandcentral
self.clear_caches()
+ _gvDialingStrRe = re.compile("This may take a few seconds", re.M)
+ _clicktocallURL = "https://www.google.com/voice/m/sendcall"
+
def dial(self, number):
"""
This is the main function responsible for initating the callback
return True
+ _sendSmsURL = "https://www.google.com/voice/m/sendsms"
+
def send_sms(self, number, message):
number = self._send_validation(number)
try:
otherData = {
'Referer' : 'https://google.com/voice/m/sms',
}
- smsSuccessPage = self._browser.download(self._smsURL, smsData, None, otherData)
+ smsSuccessPage = self._browser.download(self._sendSmsURL, smsData, None, otherData)
except urllib2.URLError, e:
warnings.warn(traceback.format_exc())
- raise RuntimeError("%s is not accesible" % self._clicktocallURL)
+ raise RuntimeError("%s is not accesible" % self._sendSmsURL)
return True
def clear_caches(self):
self.__contacts = None
+ _validateRe = re.compile("^[0-9]{10,}$")
+
def is_valid_syntax(self, number):
"""
@returns If This number be called ( syntax validation only )
return {}
+ _setforwardURL = "https://www.google.com//voice/m/setphone"
+
def set_callback_number(self, callbacknumber):
"""
Set the number that grandcental calls
def factory_name():
return "Google Voice"
+ _contactsRe = re.compile(r"""<a href="/voice/m/contact/(\d+)">(.*?)</a>""", re.S)
+ _contactsNextRe = re.compile(r""".*<a href="/voice/m/contacts(\?p=\d+)">Next.*?</a>""", re.S)
+ _contactsURL = "https://www.google.com/voice/mobile/contacts"
+
def get_contacts(self):
"""
@returns Iterable of (contact id, contact name)
contactsPage = self._browser.download(contactsPageUrl)
except urllib2.URLError, e:
warnings.warn(traceback.format_exc())
- raise RuntimeError("%s is not accesible" % self._clicktocallURL)
+ raise RuntimeError("%s is not accesible" % contactsPageUrl)
for contact_match in self._contactsRe.finditer(contactsPage):
contactId = contact_match.group(1)
contactName = saxutils.unescape(contact_match.group(2))
for contact in self.__contacts:
yield contact
+ _contactDetailPhoneRe = re.compile(r"""<div.*?>([0-9\-\(\) \t]+?)<span.*?>\((\w+)\)</span>""", re.S)
+ _contactDetailURL = "https://www.google.com/voice/mobile/contact"
+
def get_contact_details(self, contactId):
"""
@returns Iterable of (Phone Type, Phone Number)
detailPage = self._browser.download(self._contactDetailURL + '/' + contactId)
except urllib2.URLError, e:
warnings.warn(traceback.format_exc())
- raise RuntimeError("%s is not accesible" % self._clicktocallURL)
+ raise RuntimeError("%s is not accesible" % self._contactDetailURL)
for detail_match in self._contactDetailPhoneRe.finditer(detailPage):
phoneNumber = detail_match.group(1)
phoneType = saxutils.unescape(detail_match.group(2))
yield (phoneType, phoneNumber)
+ _voicemailURL = "https://www.google.com/voice/inbox/recent/voicemail/"
+ _smsURL = "https://www.google.com/voice/inbox/recent/sms/"
+
def get_messages(self):
try:
voicemailPage = self._browser.download(self._voicemailURL)
except urllib2.URLError, e:
warnings.warn(traceback.format_exc())
raise RuntimeError("%s is not accesible" % self._voicemailURL)
+ voicemailHtml = self._grab_html(voicemailPage)
+ parsedVoicemail = self._parse_voicemail(voicemailHtml)
+ decoratedVoicemails = self._decorate_voicemail(parsedVoicemail)
try:
smsPage = self._browser.download(self._smsURL)
except urllib2.URLError, e:
warnings.warn(traceback.format_exc())
raise RuntimeError("%s is not accesible" % self._smsURL)
+ smsHtml = self._grab_html(smsPage)
+ parsedSms = self._parse_sms(smsHtml)
+ decoratedSms = self._decorate_sms(parsedSms)
- voicemailHtml = self._grab_html(voicemailPage)
- parsedVoicemail = self._parse_voicemail(voicemailHtml)
- decoratedVoicemails = self._decorated_voicemail(parsedVoicemail)
-
- # @todo Parse this
- # smsHtml = self._grab_html(smsPage)
-
- allMessages = itertools.chain(decoratedVoicemails)
+ allMessages = itertools.chain(decoratedVoicemails, decoratedSms)
sortedMessages = list(allMessages)
for exactDate, header, number, relativeDate, message in sortedMessages:
yield header, number, relativeDate, message
flatHtml = htmlElement.text
return flatHtml
+ _tokenRe = re.compile(r"""<input.*?name="_rnr_se".*?value="(.*?)"\s*/>""")
+ _accountNumRe = re.compile(r"""<b class="ms\d">(.{14})</b></div>""")
+ _callbackRe = re.compile(r"""\s+(.*?):\s*(.*?)<br\s*/>\s*$""", re.M)
+ _forwardURL = "https://www.google.com/voice/mobile/phones"
+
def _grab_account_info(self):
page = self._browser.download(self._forwardURL)
number = number[1:]
return number
+ _recentCallsURL = "https://www.google.com/voice/inbox/recent/"
+ _placedCallsURL = "https://www.google.com/voice/inbox/recent/placed/"
+ _receivedCallsURL = "https://www.google.com/voice/inbox/recent/received/"
+ _missedCallsURL = "https://www.google.com/voice/inbox/recent/missed/"
+
def _get_recent(self):
"""
@returns Iterable of (personsName, phoneNumber, exact date, relative date, action)
flatXml = self._browser.download(url)
except urllib2.URLError, e:
warnings.warn(traceback.format_exc())
- raise RuntimeError("%s is not accesible" % self._clicktocallURL)
+ raise RuntimeError("%s is not accesible" % url)
allRecentData = self._grab_json(flatXml)
for recentCallData in allRecentData["messages"].itervalues():
action = saxutils.unescape(action)
yield "", number, exactDate, relativeDate, action
+ _seperateVoicemailsRegex = re.compile(r"""^\s*<div id="(\w+)"\s* class="gc-message.*?">""", re.MULTILINE | re.DOTALL)
+ _exactVoicemailTimeRegex = re.compile(r"""<span class="gc-message-time">(.*?)</span>""", re.MULTILINE)
+ _relativeVoicemailTimeRegex = re.compile(r"""<span class="gc-message-relative">(.*?)</span>""", re.MULTILINE)
+ _voicemailNumberRegex = re.compile(r"""<input type="hidden" class="gc-text gc-quickcall-ac" value="(.*?)"/>""", re.MULTILINE)
+ _prettyVoicemailNumberRegex = re.compile(r"""<span class="gc-message-type">(.*?)</span>""", re.MULTILINE)
+ _voicemailLocationRegex = re.compile(r"""<span class="gc-message-location">(.*?)</span>""", re.MULTILINE)
+ _voicemailMessageRegex = re.compile(r"""<span class="gc-word-(.*?)">(.*?)</span>""", re.MULTILINE)
+
def _parse_voicemail(self, voicemailHtml):
splitVoicemail = self._seperateVoicemailsRegex.split(voicemailHtml)
for id, messageHtml in itergroup(splitVoicemail[1:], 2):
exactTimeGroup = self._exactVoicemailTimeRegex.search(messageHtml)
- exactTime = exactTimeGroup.group(1) if exactTimeGroup else ""
+ exactTime = exactTimeGroup.group(1).strip() if exactTimeGroup else ""
relativeTimeGroup = self._relativeVoicemailTimeRegex.search(messageHtml)
- relativeTime = relativeTimeGroup.group(1) if relativeTimeGroup else ""
+ relativeTime = relativeTimeGroup.group(1).strip() if relativeTimeGroup else ""
locationGroup = self._voicemailLocationRegex.search(messageHtml)
- location = locationGroup.group(1) if locationGroup else ""
+ location = locationGroup.group(1).strip() if locationGroup else ""
+
numberGroup = self._voicemailNumberRegex.search(messageHtml)
- number = numberGroup.group(1) if numberGroup else ""
+ number = numberGroup.group(1).strip() if numberGroup else ""
prettyNumberGroup = self._prettyVoicemailNumberRegex.search(messageHtml)
- prettyNumber = prettyNumberGroup.group(1) if prettyNumberGroup else ""
+ prettyNumber = prettyNumberGroup.group(1).strip() if prettyNumberGroup else ""
+
messageGroups = self._voicemailMessageRegex.finditer(messageHtml)
messageParts = (
- (group.group(1), group.group(2))
+ (group.group(1).strip(), group.group(2).strip())
for group in messageGroups
) if messageGroups else ()
+
yield {
- "id": id,
+ "id": id.strip(),
"time": exactTime,
"relTime": relativeTime,
"prettyNumber": prettyNumber,
"messageParts": messageParts,
}
- def _decorated_voicemail(self, parsedVoicemail):
+ def _decorate_voicemail(self, parsedVoicemail):
messagePartFormat = {
"med1": "<i>%s</i>",
"med2": "%s",
message = "No Transcription"
yield exactTime, header, voicemailData["number"], voicemailData["relTime"], message
+ _smsFromRegex = re.compile(r"""<span class="gc-message-sms-from">(.*?)</span>""", re.MULTILINE | re.DOTALL)
+ _smsTextRegex = re.compile(r"""<span class="gc-message-sms-time">(.*?)</span>""", re.MULTILINE | re.DOTALL)
+ _smsTimeRegex = re.compile(r"""<span class="gc-message-sms-text">(.*?)</span>""", re.MULTILINE | re.DOTALL)
+
+ def _parse_sms(self, smsHtml):
+ splitSms = self._seperateVoicemailsRegex.split(smsHtml)
+ for id, messageHtml in itergroup(splitSms[1:], 2):
+ exactTimeGroup = self._exactVoicemailTimeRegex.search(messageHtml)
+ exactTime = exactTimeGroup.group(1).strip() if exactTimeGroup else ""
+ relativeTimeGroup = self._relativeVoicemailTimeRegex.search(messageHtml)
+ relativeTime = relativeTimeGroup.group(1).strip() if relativeTimeGroup else ""
+
+ numberGroup = self._voicemailNumberRegex.search(messageHtml)
+ number = numberGroup.group(1).strip() if numberGroup else ""
+ prettyNumberGroup = self._prettyVoicemailNumberRegex.search(messageHtml)
+ prettyNumber = prettyNumberGroup.group(1).strip() if prettyNumberGroup else ""
+
+ fromGroups = self._smsFromRegex.finditer(messageHtml)
+ fromParts = (group.group(1).strip() for group in fromGroups)
+ textGroups = self._smsTextRegex.finditer(messageHtml)
+ textParts = (group.group(1).strip() for group in textGroups)
+ timeGroups = self._smsTimeRegex.finditer(messageHtml)
+ timeParts = (group.group(1).strip() for group in timeGroups)
+
+ messageParts = itertools.izip(fromParts, textParts, timeParts)
+
+ yield {
+ "id": id.strip(),
+ "time": exactTime,
+ "relTime": relativeTime,
+ "prettyNumber": prettyNumber,
+ "number": number,
+ "messageParts": messageParts,
+ }
+
+ def _decorate_sms(self, parsedSms):
+ for messageData in parsedSms:
+ exactTime = messageData["time"] # @todo Parse This
+ header = "%s" % (messageData["prettyNumber"])
+ number = messageData["number"]
+ relativeTime = messageData["relTime"]
+ message = "\n".join((
+ "<b>%s (%s)</b>: %s" % messagePart
+ for messagePart in messageData["messageParts"]
+ ))
+ if not message:
+ message = "No Transcription"
+ yield exactTime, header, number, relativeTime, message
+
def test_backend(username, password):
import pprint