4 DialCentral - Front end for Google's GoogleVoice service.
5 Copyright (C) 2008 Eric Warnke ericew AT gmail DOT com
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 Google Voice backend code
24 http://thatsmith.com/2009/03/google-voice-addon-for-firefox/
25 http://posttopic.com/topic/google-voice-add-on-development
28 from __future__ import with_statement
33 from gvoice import gvoice
35 from dialcentral.util import io as io_utils
38 _moduleLogger = logging.getLogger(__name__)
41 class GVDialer(object):
43 MESSAGE_TEXTS = "Text"
44 MESSAGE_VOICEMAILS = "Voicemail"
47 HISTORY_RECEIVED = "Received"
48 HISTORY_MISSED = "Missed"
49 HISTORY_PLACED = "Placed"
52 def __init__(self, cookieFile = None):
53 self._gvoice = gvoice.GVoiceBackend(cookieFile)
60 def is_quick_login_possible(self):
62 @returns True then refresh_account_info might be enough to login, else full login is required
64 return self._gvoice.is_quick_login_possible()
66 def refresh_account_info(self):
67 return self._gvoice.refresh_account_info()
69 def login(self, username, password):
71 Attempt to login to GoogleVoice
72 @returns Whether login was successful or not
74 return self._gvoice.login(username, password)
82 return self._gvoice.logout()
85 return self._gvoice.persist()
88 return self._gvoice.is_dnd()
90 def set_dnd(self, doNotDisturb):
91 return self._gvoice.set_dnd(doNotDisturb)
93 def call(self, outgoingNumber):
95 This is the main function responsible for initating the callback
97 return self._gvoice.call(outgoingNumber)
99 def cancel(self, outgoingNumber=None):
101 Cancels a call matching outgoing and forwarding numbers (if given).
102 Will raise an error if no matching call is being placed
104 return self._gvoice.cancel(outgoingNumber)
106 def send_sms(self, phoneNumbers, message):
107 self._gvoice.send_sms(phoneNumbers, message)
109 def search(self, query):
111 Search your Google Voice Account history for calls, voicemails, and sms
112 Returns ``Folder`` instance containting matching messages
114 return self._gvoice.search(query)
116 def get_feed(self, feed):
117 return self._gvoice.get_feed(feed)
119 def download(self, messageId, targetPath):
121 Download a voicemail or recorded call MP3 matching the given ``msg``
122 which can either be a ``Message`` instance, or a SHA1 identifier.
123 Message hashes can be found in ``self.voicemail().messages`` for example.
124 Returns location of saved file.
126 self._gvoice.download(messageId, targetPath)
128 def is_valid_syntax(self, number):
130 @returns If This number be called ( syntax validation only )
132 return self._gvoice.is_valid_syntax(number)
134 def get_account_number(self):
136 @returns The GoogleVoice phone number
138 return self._gvoice.get_account_number()
140 def get_callback_numbers(self):
142 @returns a dictionary mapping call back numbers to descriptions
143 @note These results are cached for 30 minutes.
145 return self._gvoice.get_callback_numbers()
147 def set_callback_number(self, callbacknumber):
149 Set the number that GoogleVoice calls
150 @param callbacknumber should be a proper 10 digit number
152 return self._gvoice.set_callback_number(callbacknumber)
154 def get_callback_number(self):
156 @returns Current callback number or None
158 return self._gvoice.get_callback_number()
160 def get_call_history(self, historyType):
162 @returns Iterable of (personsName, phoneNumber, exact date, relative date, action)
164 history = list(self._get_call_history(historyType))
165 history.sort(key=lambda item: item["time"])
168 def _get_call_history(self, historyType):
170 @returns Iterable of (personsName, phoneNumber, exact date, relative date, action)
172 if historyType in [self.HISTORY_RECEIVED, self.HISTORY_ALL] or not self._received:
173 self._received = list(self._gvoice.get_received_calls())
174 for item in self._received:
175 item["action"] = self.HISTORY_RECEIVED
176 if historyType in [self.HISTORY_MISSED, self.HISTORY_ALL] or not self._missed:
177 self._missed = list(self._gvoice.get_missed_calls())
178 for item in self._missed:
179 item["action"] = self.HISTORY_MISSED
180 if historyType in [self.HISTORY_PLACED, self.HISTORY_ALL] or not self._placed:
181 self._placed = list(self._gvoice.get_placed_calls())
182 for item in self._placed:
183 item["action"] = self.HISTORY_PLACED
184 received = self._received
185 missed = self._missed
186 placed = self._placed
187 for item in received:
194 def get_messages(self, messageType):
195 messages = list(self._get_messages(messageType))
196 messages.sort(key=lambda message: message["time"])
199 def _get_messages(self, messageType):
200 if messageType in [self.MESSAGE_VOICEMAILS, self.MESSAGE_ALL] or not self._voicemails:
201 self._voicemails = list(self._gvoice.get_voicemails())
202 if messageType in [self.MESSAGE_TEXTS, self.MESSAGE_ALL] or not self._texts:
203 self._texts = list(self._gvoice.get_texts())
204 voicemails = self._voicemails
207 conversations = itertools.chain(voicemails, smss)
208 for conversation in conversations:
209 messages = conversation.messages
211 (message.whoFrom, self._format_message(message), message.when)
212 for message in messages
216 "id": conversation.id,
217 "contactId": conversation.contactId,
218 "name": conversation.name,
219 "time": conversation.time,
220 "relTime": conversation.relTime,
221 "prettyNumber": conversation.prettyNumber,
222 "number": conversation.number,
223 "location": conversation.location,
224 "messageParts": messageParts,
225 "type": conversation.type,
226 "isRead": conversation.isRead,
227 "isTrash": conversation.isTrash,
228 "isSpam": conversation.isSpam,
229 "isArchived": conversation.isArchived,
233 def clear_caches(self):
236 def get_addressbooks(self):
238 @returns Iterable of (Address Book Factory, Book Id, Book Name)
242 def open_addressbook(self, bookId):
246 def contact_source_short_name(contactId):
251 return "Google Voice"
253 def _format_message(self, message):
254 messagePartFormat = {
260 messagePartFormat[text.accuracy] % io_utils.escape(text.text)
261 for text in message.body
265 def sort_messages(allMessages):
266 sortableAllMessages = [
267 (message["time"], message)
268 for message in allMessages
270 sortableAllMessages.sort(reverse=True)
273 for (exactTime, message) in sortableAllMessages
277 def decorate_recent(recentCallData):
279 @returns (personsName, phoneNumber, date, action)
281 contactId = recentCallData["contactId"]
282 if recentCallData["name"]:
283 header = recentCallData["name"]
284 elif recentCallData["prettyNumber"]:
285 header = recentCallData["prettyNumber"]
286 elif recentCallData["location"]:
287 header = recentCallData["location"]
291 number = recentCallData["number"]
292 relTime = recentCallData["relTime"]
293 action = recentCallData["action"]
294 return contactId, header, number, relTime, action
297 def decorate_message(messageData):
298 contactId = messageData["contactId"]
299 exactTime = messageData["time"]
300 if messageData["name"]:
301 header = messageData["name"]
302 elif messageData["prettyNumber"]:
303 header = messageData["prettyNumber"]
306 number = messageData["number"]
307 relativeTime = messageData["relTime"]
309 messageParts = list(messageData["messageParts"])
310 if len(messageParts) == 0:
311 messages = ("No Transcription", )
312 elif len(messageParts) == 1:
313 messages = (messageParts[0][1], )
316 "<b>%s</b>: %s" % (messagePart[0], messagePart[1])
317 for messagePart in messageParts
320 decoratedResults = contactId, header, number, relativeTime, messages
321 return decoratedResults