Implementing caches for a session
[gc-dialer] / src / backends / gv_backend.py
1 #!/usr/bin/python
2
3 """
4 DialCentral - Front end for Google's GoogleVoice service.
5 Copyright (C) 2008  Eric Warnke ericew AT gmail DOT com
6
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.
11
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.
16
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
20
21 Google Voice backend code
22
23 Resources
24         http://thatsmith.com/2009/03/google-voice-addon-for-firefox/
25         http://posttopic.com/topic/google-voice-add-on-development
26 """
27
28 from __future__ import with_statement
29
30 import itertools
31 import logging
32
33 from gvoice import gvoice
34
35
36 _moduleLogger = logging.getLogger(__name__)
37
38
39 class GVDialer(object):
40
41         def __init__(self, cookieFile = None):
42                 self._gvoice = gvoice.GVoiceBackend(cookieFile)
43
44         def is_quick_login_possible(self):
45                 """
46                 @returns True then is_authed might be enough to login, else full login is required
47                 """
48                 return self._gvoice.is_quick_login_possible()
49
50         def is_authed(self, force = False):
51                 """
52                 Attempts to detect a current session
53                 @note Once logged in try not to reauth more than once a minute.
54                 @returns If authenticated
55                 """
56                 return self._gvoice.is_authed(force)
57
58         def login(self, username, password):
59                 """
60                 Attempt to login to GoogleVoice
61                 @returns Whether login was successful or not
62                 """
63                 return self._gvoice.login(username, password)
64
65         def logout(self):
66                 return self._gvoice.logout()
67
68         def persist(self):
69                 return self._gvoice.persist()
70
71         def is_dnd(self):
72                 return self._gvoice.is_dnd()
73
74         def set_dnd(self, doNotDisturb):
75                 return self._gvoice.set_dnd(doNotDisturb)
76
77         def call(self, outgoingNumber):
78                 """
79                 This is the main function responsible for initating the callback
80                 """
81                 return self._gvoice.call(outgoingNumber)
82
83         def cancel(self, outgoingNumber=None):
84                 """
85                 Cancels a call matching outgoing and forwarding numbers (if given). 
86                 Will raise an error if no matching call is being placed
87                 """
88                 return self._gvoice.cancel(outgoingNumber)
89
90         def send_sms(self, phoneNumbers, message):
91                 self._gvoice.send_sms(phoneNumbers, message)
92
93         def search(self, query):
94                 """
95                 Search your Google Voice Account history for calls, voicemails, and sms
96                 Returns ``Folder`` instance containting matching messages
97                 """
98                 return self._gvoice.search(query)
99
100         def get_feed(self, feed):
101                 return self._gvoice.get_feed(feed)
102
103         def download(self, messageId, adir):
104                 """
105                 Download a voicemail or recorded call MP3 matching the given ``msg``
106                 which can either be a ``Message`` instance, or a SHA1 identifier. 
107                 Saves files to ``adir`` (defaults to current directory). 
108                 Message hashes can be found in ``self.voicemail().messages`` for example. 
109                 Returns location of saved file.
110                 """
111                 return self._gvoice.download(messageId, adir)
112
113         def is_valid_syntax(self, number):
114                 """
115                 @returns If This number be called ( syntax validation only )
116                 """
117                 return self._gvoice.is_valid_syntax(number)
118
119         def get_account_number(self):
120                 """
121                 @returns The GoogleVoice phone number
122                 """
123                 return self._gvoice.get_account_number()
124
125         def get_callback_numbers(self):
126                 """
127                 @returns a dictionary mapping call back numbers to descriptions
128                 @note These results are cached for 30 minutes.
129                 """
130                 return self._gvoice.get_callback_numbers()
131
132         def set_callback_number(self, callbacknumber):
133                 """
134                 Set the number that GoogleVoice calls
135                 @param callbacknumber should be a proper 10 digit number
136                 """
137                 return self._gvoice.set_callback_number(callbacknumber)
138
139         def get_callback_number(self):
140                 """
141                 @returns Current callback number or None
142                 """
143                 return self._gvoice.get_callback_number()
144
145         def get_recent(self):
146                 """
147                 @returns Iterable of (personsName, phoneNumber, exact date, relative date, action)
148                 """
149                 return list(self._gvoice.get_recent())
150
151         def get_contacts(self):
152                 """
153                 @returns Fresh dictionary of items
154                 """
155                 return dict(self._gvoice.get_contacts())
156
157         def get_messages(self):
158                 return list(self._get_messages())
159
160         def _get_messages(self):
161                 voicemails = self._gvoice.get_voicemails()
162                 smss = self._gvoice.get_texts()
163                 conversations = itertools.chain(voicemails, smss)
164                 for conversation in conversations:
165                         messages = conversation.messages
166                         messageParts = [
167                                 (message.whoFrom, self._format_message(message), message.when)
168                                 for message in messages
169                         ]
170
171                         messageDetails = {
172                                 "id": conversation.id,
173                                 "contactId": conversation.contactId,
174                                 "name": conversation.name,
175                                 "time": conversation.time,
176                                 "relTime": conversation.relTime,
177                                 "prettyNumber": conversation.prettyNumber,
178                                 "number": conversation.number,
179                                 "location": conversation.location,
180                                 "messageParts": messageParts,
181                                 "type": conversation.type,
182                                 "isRead": conversation.isRead,
183                                 "isTrash": conversation.isTrash,
184                                 "isSpam": conversation.isSpam,
185                                 "isArchived": conversation.isArchived,
186                         }
187                         yield messageDetails
188
189         def clear_caches(self):
190                 pass
191
192         def get_addressbooks(self):
193                 """
194                 @returns Iterable of (Address Book Factory, Book Id, Book Name)
195                 """
196                 yield self, "", ""
197
198         def open_addressbook(self, bookId):
199                 return self
200
201         @staticmethod
202         def contact_source_short_name(contactId):
203                 return "GV"
204
205         @staticmethod
206         def factory_name():
207                 return "Google Voice"
208
209         def _format_message(self, message):
210                 messagePartFormat = {
211                         "med1": "<i>%s</i>",
212                         "med2": "%s",
213                         "high": "<b>%s</b>",
214                 }
215                 return " ".join(
216                         messagePartFormat[text.accuracy] % text.text
217                         for text in message.body
218                 )
219
220
221 def sort_messages(allMessages):
222         sortableAllMessages = [
223                 (message["time"], message)
224                 for message in allMessages
225         ]
226         sortableAllMessages.sort(reverse=True)
227         return (
228                 message
229                 for (exactTime, message) in sortableAllMessages
230         )
231
232
233 def decorate_recent(recentCallData):
234         """
235         @returns (personsName, phoneNumber, date, action)
236         """
237         contactId = recentCallData["contactId"]
238         if recentCallData["name"]:
239                 header = recentCallData["name"]
240         elif recentCallData["prettyNumber"]:
241                 header = recentCallData["prettyNumber"]
242         elif recentCallData["location"]:
243                 header = recentCallData["location"]
244         else:
245                 header = "Unknown"
246
247         number = recentCallData["number"]
248         relTime = recentCallData["relTime"]
249         action = recentCallData["action"]
250         return contactId, header, number, relTime, action
251
252
253 def decorate_message(messageData):
254         contactId = messageData["contactId"]
255         exactTime = messageData["time"]
256         if messageData["name"]:
257                 header = messageData["name"]
258         elif messageData["prettyNumber"]:
259                 header = messageData["prettyNumber"]
260         else:
261                 header = "Unknown"
262         number = messageData["number"]
263         relativeTime = messageData["relTime"]
264
265         messageParts = list(messageData["messageParts"])
266         if len(messageParts) == 0:
267                 messages = ("No Transcription", )
268         elif len(messageParts) == 1:
269                 messages = (messageParts[0][1], )
270         else:
271                 messages = [
272                         "<b>%s</b>: %s" % (messagePart[0], messagePart[1])
273                         for messagePart in messageParts
274                 ]
275
276         decoratedResults = contactId, header, number, relativeTime, messages
277         return decoratedResults