self._browser.cookies.load()
self._token = ""
- self._accountNum = None
+ self._accountNum = ""
self._lastAuthed = 0.0
self._callbackNumber = ""
self._callbackNumbers = {}
@returns If authenticated
"""
- if (time.time() - self._lastAuthed) < 60 and not force:
+ if (time.time() - self._lastAuthed) < 120 and not force:
return True
try:
@returns a dictionary mapping call back numbers to descriptions
@note These results are cached for 30 minutes.
"""
- if time.time() - self._lastAuthed < 1800 or self.is_authed():
- return self._callbackNumbers
-
- return {}
+ if not self.is_authed():
+ return {}
+ return self._callbackNumbers
_setforwardURL = "https://www.google.com//voice/m/setphone"
@param callbacknumber should be a proper 10 digit number
"""
self._callbackNumber = callbacknumber
- callbackPostData = urllib.urlencode({
- '_rnr_se': self._token,
- 'phone': callbacknumber
- })
- try:
- callbackSetPage = self._browser.download(self._setforwardURL, callbackPostData)
- except urllib2.URLError, e:
- warnings.warn(traceback.format_exc())
- raise RuntimeError("%s is not accesible" % self._setforwardURL)
- # @bug This does not seem to be keeping on my tablet (but works on the
- # desktop), or the reading isn't working too well
- self._browser.cookies.save()
+ # Currently this isn't working out in GoogleVoice, but thats ok, we pass the callback on dial
+ #callbackPostData = urllib.urlencode({
+ # '_rnr_se': self._token,
+ # 'phone': callbacknumber
+ #})
+ #try:
+ # callbackSetPage = self._browser.download(self._setforwardURL, callbackPostData)
+ # self._browser.cookies.save()
+ #except urllib2.URLError, e:
+ # warnings.warn(traceback.format_exc())
+ # raise RuntimeError("%s is not accesible" % self._setforwardURL)
+
return True
def get_callback_number(self):
"""
@returns Current callback number or None
"""
- for c in self._browser.cookies:
- if c.name == "gv-ph":
- return c.value
+ #for c in self._browser.cookies:
+ # if c.name == "gv-ph":
+ # return c.value
return self._callbackNumber
def get_recent(self):
"""
- @todo Sort this stuff
@returns Iterable of (personsName, phoneNumber, date, action)
"""
sortedRecent = [
for contact in self.__contacts:
yield contact
- _contactDetailPhoneRe = re.compile(r"""<div.*?>([0-9\-\(\) \t]+?)<span.*?>\((\w+)\)</span>""", re.S)
+ _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):
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)
+ sortedMessages.sort(reverse=True)
for exactDate, header, number, relativeDate, message in sortedMessages:
yield header, number, relativeDate, message
self._token = tokenGroup.group(1)
anGroup = self._accountNumRe.search(page)
- if anGroup is None:
- raise RuntimeError("Could not extract account number from GoogleVoice")
- self._accountNum = anGroup.group(1)
+ if anGroup is not None:
+ self._accountNum = anGroup.group(1)
+ else:
+ warnings.warn("Could not extract account number from GoogleVoice", UserWarning, 2)
self._callbackNumbers = {}
for match in self._callbackRe.finditer(page):
"""
@returns Iterable of (personsName, phoneNumber, exact date, relative date, action)
"""
- for url in (
- self._receivedCallsURL,
- self._missedCallsURL,
- self._placedCallsURL,
+ for action, url in (
+ ("Received", self._receivedCallsURL),
+ ("Missed", self._missedCallsURL),
+ ("Placed", self._placedCallsURL),
):
try:
flatXml = self._browser.download(url)
warnings.warn(traceback.format_exc())
raise RuntimeError("%s is not accesible" % url)
- allRecentData = self._grab_json(flatXml)
- for recentCallData in allRecentData["messages"].itervalues():
- number = recentCallData["displayNumber"]
- exactDate = recentCallData["displayStartDateTime"]
- relativeDate = recentCallData["relativeStartTime"]
- action = ", ".join((
- label.title()
- for label in recentCallData["labels"]
- if label.lower() != "all" and label.lower() != "inbox"
- ))
- number = saxutils.unescape(number)
- exactDate = saxutils.unescape(exactDate)
- exactDate = datetime.datetime.strptime(exactDate, "%m/%d/%y %I:%M %p")
- relativeDate = saxutils.unescape(relativeDate)
- 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)
+ allRecentHtml = self._grab_html(flatXml)
+ allRecentData = self._parse_voicemail(allRecentHtml)
+ for recentCallData in allRecentData:
+ exactTime = recentCallData["time"]
+ if recentCallData["name"]:
+ header = recentCallData["name"]
+ elif recentCallData["prettyNumber"]:
+ header = recentCallData["prettyNumber"]
+ elif recentCallData["location"]:
+ header = recentCallData["location"]
+ else:
+ header = "Unknown"
+ yield header, recentCallData["number"], exactTime, recentCallData["relTime"], 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)
+ _voicemailNameRegex = re.compile(r"""<a class=.*?gc-message-name-link.*?>(.*?)</a>""", re.MULTILINE | re.DOTALL)
_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)
+ _voicemailLocationRegex = re.compile(r"""<span class="gc-message-location">.*?<a.*?>(.*?)</a></span>""", re.MULTILINE)
+ _voicemailMessageRegex = re.compile(r"""<span id="\d+-\d+" class="gc-word-(.*?)">(.*?)</span>""", re.MULTILINE)
def _parse_voicemail(self, voicemailHtml):
splitVoicemail = self._seperateVoicemailsRegex.split(voicemailHtml)
- for id, messageHtml in itergroup(splitVoicemail[1:], 2):
+ for messageId, 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 ""
+ exactTime = datetime.datetime.strptime(exactTime, "%m/%d/%y %I:%M %p")
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 ""
+
+ nameGroup = self._voicemailNameRegex.search(messageHtml)
+ name = nameGroup.group(1).strip() if nameGroup 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": messageId.strip(),
+ "name": name,
"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",
"high": "<b>%s</b>",
}
for voicemailData in parsedVoicemail:
- exactTime = voicemailData["time"] # @todo Parse This
- header = "%s %s" % (voicemailData["prettyNumber"], voicemailData["location"])
+ exactTime = voicemailData["time"]
+ if voicemailData["name"]:
+ header = voicemailData["name"]
+ elif voicemailData["prettyNumber"]:
+ header = voicemailData["prettyNumber"]
+ elif voicemailData["location"]:
+ header = voicemailData["location"]
+ else:
+ header = "Unknown"
message = " ".join((
messagePartFormat[quality] % part
for (quality, part) in voicemailData["messageParts"]
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 messageId, messageHtml in itergroup(splitSms[1:], 2):
+ exactTimeGroup = self._exactVoicemailTimeRegex.search(messageHtml)
+ exactTime = exactTimeGroup.group(1).strip() if exactTimeGroup else ""
+ exactTime = datetime.datetime.strptime(exactTime, "%m/%d/%y %I:%M %p")
+ relativeTimeGroup = self._relativeVoicemailTimeRegex.search(messageHtml)
+ relativeTime = relativeTimeGroup.group(1).strip() if relativeTimeGroup else ""
+
+ nameGroup = self._voicemailNameRegex.search(messageHtml)
+ name = nameGroup.group(1).strip() if nameGroup 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": messageId.strip(),
+ "name": name,
+ "time": exactTime,
+ "relTime": relativeTime,
+ "prettyNumber": prettyNumber,
+ "number": number,
+ "messageParts": messageParts,
+ }
+
+ def _decorate_sms(self, parsedSms):
+ for messageData in parsedSms:
+ exactTime = messageData["time"]
+ if messageData["name"]:
+ header = messageData["name"]
+ elif messageData["prettyNumber"]:
+ header = messageData["prettyNumber"]
+ else:
+ header = "Unknown"
+ number = messageData["number"]
+ relativeTime = messageData["relTime"]
+ message = "\n".join((
+ "<b>%s</b>: %s" % (messagePart[0], messagePart[-1])
+ for messagePart in messageData["messageParts"]
+ ))
+ if not message:
+ message = "No Transcription"
+ yield exactTime, header, number, relativeTime, message
+
def test_backend(username, password):
import pprint
print "Authenticated: ", backend.is_authed()
print "Login?: ", backend.login(username, password)
print "Authenticated: ", backend.is_authed()
- print "Token: ", backend._token
+ # print "Token: ", backend._token
print "Account: ", backend.get_account_number()
print "Callback: ", backend.get_callback_number()
# print "All Callback: ",