From 5dd907dbde6707ba2f79cc373a3e30b63302ebed Mon Sep 17 00:00:00 2001 From: epage Date: Wed, 20 May 2009 02:44:47 +0000 Subject: [PATCH] First step towards displaying of voicemail message git-svn-id: file:///svnroot/gc-dialer/trunk@294 c39d3808-3fe2-4d86-a59f-b7f623ee9f21 --- src/dialcentral.glade | 5 +++ src/gc_views.py | 27 +++++++++---- src/gv_backend.py | 106 ++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 121 insertions(+), 17 deletions(-) diff --git a/src/dialcentral.glade b/src/dialcentral.glade index 17cd93c..5f260f9 100644 --- a/src/dialcentral.glade +++ b/src/dialcentral.glade @@ -904,6 +904,7 @@ Number: True True + True False @@ -993,8 +994,11 @@ Number: 5 + Send SMS True center + 800 + 300 normal True True @@ -1012,6 +1016,7 @@ Number: True True + True False diff --git a/src/gc_views.py b/src/gc_views.py index b751451..3a6b3da 100644 --- a/src/gc_views.py +++ b/src/gc_views.py @@ -354,12 +354,16 @@ class PhoneTypeSelector(object): self._typemodel.clear() for phoneType, phoneNumber in contactDetails: + # @bug this isn't populating correctly for recent and messages but it is for contacts + print repr(phoneNumber), repr(phoneType) self._typemodel.append((phoneNumber, "%s - %s" % (make_pretty(phoneNumber), phoneType))) + # @todo Need to decide how how to handle the single phone number case if message: + self._message.set_markup(message) self._message.show() - self._message.set_text(message) else: + self._message.set_markup("") self._message.hide() userResponse = self._dialog.run() @@ -388,7 +392,7 @@ class PhoneTypeSelector(object): def _on_phonetype_send_sms(self, *args): self._dialog.response(gtk.RESPONSE_CANCEL) idly_run = gtk_toolbox.asynchronous_gtk_message(self._smsDialog.run) - idly_run(self._get_number(), self._message.get_text()) + idly_run(self._get_number(), self._message.get_label()) def _on_phonetype_select(self, *args): self._dialog.response(gtk.RESPONSE_OK) @@ -419,9 +423,10 @@ class SmsEntryDialog(object): def run(self, number, message = ""): if message: + self._message.set_markup(message) self._message.show() - self._message.set_text(message) else: + self._message.set_markup("") self._message.hide() self._smsEntry.get_buffer().set_text("") self._update_letter_count() @@ -613,6 +618,7 @@ class RecentCallsView(object): self._onRecentviewRowActivatedId = 0 textrenderer = gtk.CellRendererText() + # @todo Make seperate columns for each item in recent item payload self._recentviewColumn = gtk.TreeViewColumn("Calls") self._recentviewColumn.pack_start(textrenderer, expand=True) self._recentviewColumn.add_attribute(textrenderer, "text", 1) @@ -676,9 +682,11 @@ class RecentCallsView(object): if not itr: return - contactPhoneNumbers = [("Phone", self._recentmodel.get_value(itr, 0))] + number = self._recentmodel.get_value(itr, 0) + number = make_ugly(number) + contactPhoneNumbers = [("Phone", number)] description = self._recentmodel.get_value(itr, 1) - print repr(contactPhoneNumbers), repr(description) + print "Activated Recent Row:", repr(contactPhoneNumbers), repr(description) phoneNumber = self._phoneTypeSelector.run(contactPhoneNumbers, message = description) if 0 == len(phoneNumber): @@ -701,9 +709,10 @@ class MessagesView(object): self._onMessageviewRowActivatedId = 0 textrenderer = gtk.CellRendererText() + # @todo Make seperate columns for each item in message payload self._messageviewColumn = gtk.TreeViewColumn("Messages") self._messageviewColumn.pack_start(textrenderer, expand=True) - self._messageviewColumn.add_attribute(textrenderer, "text", 1) + self._messageviewColumn.add_attribute(textrenderer, "markup", 1) self._messageviewColumn.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) self._phoneTypeSelector = PhoneTypeSelector(widgetTree, self._backend) @@ -751,8 +760,10 @@ class MessagesView(object): self._messagetime = 0.0 messageItems = [] - for phoneNumber, data in messageItems: - item = (phoneNumber, data) + for header, number, relativeDate, message in messageItems: + number = make_ugly(number) + print "Discarding", header, relativeDate + item = (number, message) with gtk_toolbox.gtk_lock(): self._messagemodel.append(item) diff --git a/src/gv_backend.py b/src/gv_backend.py index b354de9..75ec4bd 100644 --- a/src/gv_backend.py +++ b/src/gv_backend.py @@ -32,6 +32,7 @@ import urllib import urllib2 import time import datetime +import itertools import warnings import traceback from xml.sax import saxutils @@ -64,6 +65,39 @@ else: return simplejson.loads(flattened) +def itergroup(iterator, count, padValue = None): + """ + Iterate in groups of 'count' values. If there + aren't enough values, the last result is padded with + None. + + >>> for val in itergroup([1, 2, 3, 4, 5, 6], 3): + ... print tuple(val) + (1, 2, 3) + (4, 5, 6) + >>> for val in itergroup([1, 2, 3, 4, 5, 6], 3): + ... print list(val) + [1, 2, 3] + [4, 5, 6] + >>> for val in itergroup([1, 2, 3, 4, 5, 6, 7], 3): + ... print tuple(val) + (1, 2, 3) + (4, 5, 6) + (7, None, None) + >>> for val in itergroup("123456", 3): + ... print tuple(val) + ('1', '2', '3') + ('4', '5', '6') + >>> for val in itergroup("123456", 3): + ... print repr("".join(val)) + '123' + '456' + """ + paddedIterator = itertools.chain(iterator, itertools.repeat(padValue, count-1)) + nIterators = (paddedIterator, ) * count + return itertools.izip(*nIterators) + + class GVDialer(object): """ This class encapsulates all of the knowledge necessary to interace with the grandcentral servers @@ -97,6 +131,14 @@ class GVDialer(object): _voicemailURL = "https://www.google.com/voice/inbox/recent/voicemail/" _smsURL = "https://www.google.com/voice/inbox/recent/sms/" + _seperateVoicemailsRegex = re.compile(r"""^\s*
""", re.MULTILINE | re.DOTALL) + _exactVoicemailTimeRegex = re.compile(r"""(.*?)""", re.MULTILINE) + _relativeVoicemailTimeRegex = re.compile(r"""(.*?)""", re.MULTILINE) + _voicemailNumberRegex = re.compile(r"""""", re.MULTILINE) + _prettyVoicemailNumberRegex = re.compile(r"""(.*?)""", re.MULTILINE) + _voicemailLocationRegex = re.compile(r"""(.*?)""", re.MULTILINE) + _voicemailMessageRegex = re.compile(r"""(.*?)""", 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) @@ -382,15 +424,16 @@ class GVDialer(object): raise RuntimeError("%s is not accesible" % self._smsURL) voicemailHtml = self._grab_html(voicemailPage) - smsHtml = self._grab_html(smsPage) + parsedVoicemail = self._parse_voicemail(voicemailHtml) + decoratedVoicemails = self._decorated_voicemail(parsedVoicemail) - print "="*60 - print voicemailHtml - print "-"*60 - print smsHtml - print "="*60 + # @todo Parse this + # smsHtml = self._grab_html(smsPage) - return () + allMessages = itertools.chain(decoratedVoicemails) + sortedMessages = list(allMessages) + for exactDate, header, number, relativeDate, message in sortedMessages: + yield header, number, relativeDate, message def _grab_json(self, flatXml): xmlTree = ElementTree.fromstring(flatXml) @@ -467,6 +510,51 @@ class GVDialer(object): action = saxutils.unescape(action) yield "", number, exactDate, relativeDate, action + 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 "" + relativeTimeGroup = self._relativeVoicemailTimeRegex.search(messageHtml) + relativeTime = relativeTimeGroup.group(1) if relativeTimeGroup else "" + locationGroup = self._voicemailLocationRegex.search(messageHtml) + location = locationGroup.group(1) if locationGroup else "" + numberGroup = self._voicemailNumberRegex.search(messageHtml) + number = numberGroup.group(1) if numberGroup else "" + prettyNumberGroup = self._prettyVoicemailNumberRegex.search(messageHtml) + prettyNumber = prettyNumberGroup.group(1) if prettyNumberGroup else "" + messageGroups = self._voicemailMessageRegex.finditer(messageHtml) + messageParts = ( + (group.group(1), group.group(2)) + for group in messageGroups + ) if messageGroups else () + yield { + "id": id, + "time": exactTime, + "relTime": relativeTime, + "prettyNumber": prettyNumber, + "number": number, + "location": location, + "messageParts": messageParts, + } + + def _decorated_voicemail(self, parsedVoicemail): + messagePartFormat = { + "med1": "%s", + "med2": "%s", + "high": "%s", + } + for voicemailData in parsedVoicemail: + exactTime = voicemailData["time"] # @todo Parse This + header = "%s %s" % (voicemailData["prettyNumber"], voicemailData["location"]) + message = " ".join(( + messagePartFormat[quality] % part + for (quality, part) in voicemailData["messageParts"] + )).strip() + if not message: + message = "No Transcription" + yield exactTime, header, voicemailData["number"], voicemailData["relTime"], message + def test_backend(username, password): import pprint @@ -477,8 +565,8 @@ def test_backend(username, password): print "Token: ", backend._token print "Account: ", backend.get_account_number() print "Callback: ", backend.get_callback_number() - print "All Callback: ", - pprint.pprint(backend.get_callback_numbers()) + # print "All Callback: ", + # pprint.pprint(backend.get_callback_numbers()) # print "Recent: ", # pprint.pprint(list(backend.get_recent())) # print "Contacts: ", -- 1.7.9.5