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