9fffe990c8513ac007087168f8f01c4adf1a0926
[theonering] / src / gvoice / session.py
1 #!/usr/bin/env python
2
3 import os
4 import time
5 import logging
6
7 import backend
8 import addressbook
9 import conversations
10 import state_machine
11
12
13 _moduleLogger = logging.getLogger("gvoice.session")
14
15
16 class Session(object):
17
18         _DEFAULTS = {
19                 "contacts": (3, "hours"),
20                 "voicemail": (30, "minutes"),
21                 "texts": (5, "minutes"),
22         }
23
24         _MINIMUM_MESSAGE_PERIOD = state_machine.to_seconds(minutes=30)
25
26         def __init__(self, cookiePath = None, defaults = None):
27                 if defaults is None:
28                         defaults = self._DEFAULTS
29                 else:
30                         for key, (quant, unit) in defaults.iteritems():
31                                 if quant == 0:
32                                         defaults[key] = self._DEFAULTS[key]
33                                 elif quant < 0:
34                                         defaults[key] = state_machine.INFINITE_PERIOD
35                 self._username = None
36                 self._password = None
37
38                 self._backend = backend.GVoiceBackend(cookiePath)
39
40                 contactsPeriodInSeconds = state_machine.to_seconds(
41                         **{defaults["contacts"][1]: defaults["contacts"][0],}
42                 )
43                 self._addressbook = addressbook.Addressbook(self._backend)
44                 self._addressbookStateMachine = state_machine.UpdateStateMachine([self.addressbook], "Addressbook")
45                 self._addressbookStateMachine.set_state_strategy(
46                         state_machine.StateMachine.STATE_DND,
47                         state_machine.NopStateStrategy()
48                 )
49                 self._addressbookStateMachine.set_state_strategy(
50                         state_machine.StateMachine.STATE_IDLE,
51                         state_machine.NopStateStrategy()
52                 )
53                 self._addressbookStateMachine.set_state_strategy(
54                         state_machine.StateMachine.STATE_ACTIVE,
55                         state_machine.ConstantStateStrategy(contactsPeriodInSeconds)
56                 )
57
58                 voicemailPeriodInSeconds = state_machine.to_seconds(
59                         **{defaults["voicemail"][1]: defaults["voicemail"][0],}
60                 )
61                 self._voicemails = conversations.Conversations(self._backend.get_voicemails)
62                 self._voicemailsStateMachine = state_machine.UpdateStateMachine([self.voicemails], "Voicemail")
63                 self._voicemailsStateMachine.set_state_strategy(
64                         state_machine.StateMachine.STATE_DND,
65                         state_machine.NopStateStrategy()
66                 )
67                 self._voicemailsStateMachine.set_state_strategy(
68                         state_machine.StateMachine.STATE_IDLE,
69                         state_machine.ConstantStateStrategy(
70                                 max(voicemailPeriodInSeconds * 4, self._MINIMUM_MESSAGE_PERIOD)
71                         )
72                 )
73                 self._voicemailsStateMachine.set_state_strategy(
74                         state_machine.StateMachine.STATE_ACTIVE,
75                         state_machine.ConstantStateStrategy(voicemailPeriodInSeconds)
76                 )
77                 self._voicemails.updateSignalHandler.register_sink(
78                         self._voicemailsStateMachine.request_reset_timers
79                 )
80
81                 textsPeriodInSeconds = state_machine.to_seconds(
82                         **{defaults["texts"][1]: defaults["texts"][0],}
83                 )
84                 self._texts = conversations.Conversations(self._backend.get_texts)
85                 self._textsStateMachine = state_machine.UpdateStateMachine([self.texts], "Texting")
86                 self._textsStateMachine.set_state_strategy(
87                         state_machine.StateMachine.STATE_DND,
88                         state_machine.NopStateStrategy()
89                 )
90                 self._textsStateMachine.set_state_strategy(
91                         state_machine.StateMachine.STATE_IDLE,
92                         state_machine.ConstantStateStrategy(
93                                 max(textsPeriodInSeconds * 4, self._MINIMUM_MESSAGE_PERIOD)
94                         )
95                 )
96                 self._textsStateMachine.set_state_strategy(
97                         state_machine.StateMachine.STATE_ACTIVE,
98                         state_machine.GeometricStateStrategy(
99                                 state_machine.to_seconds(seconds=20),
100                                 state_machine.to_seconds(seconds=1),
101                                 textsPeriodInSeconds,
102                         )
103                 )
104                 self._texts.updateSignalHandler.register_sink(
105                         self._textsStateMachine.request_reset_timers
106                 )
107
108                 self._masterStateMachine = state_machine.MasterStateMachine()
109                 self._masterStateMachine.append_machine(self._addressbookStateMachine)
110                 self._masterStateMachine.append_machine(self._voicemailsStateMachine)
111                 self._masterStateMachine.append_machine(self._textsStateMachine)
112
113                 self._lastDndCheck = 0
114                 self._cachedIsDnd = False
115
116         def load(self, path):
117                 self._texts.load(os.sep.join((path, "texts.cache")))
118                 self._voicemails.load(os.sep.join((path, "voicemails.cache")))
119
120         def save(self, path):
121                 self._texts.save(os.sep.join((path, "texts.cache")))
122                 self._voicemails.save(os.sep.join((path, "voicemails.cache")))
123
124         def close(self):
125                 self._voicemails.updateSignalHandler.unregister_sink(
126                         self._voicemailsStateMachine.request_reset_timers
127                 )
128                 self._texts.updateSignalHandler.unregister_sink(
129                         self._textsStateMachine.request_reset_timers
130                 )
131                 self._masterStateMachine.close()
132
133         def login(self, username, password):
134                 self._username = username
135                 self._password = password
136                 self._backend.login(self._username, self._password)
137
138                 self._masterStateMachine.start()
139
140         def logout(self):
141                 self._masterStateMachine.stop()
142                 self._backend.logout()
143
144                 self._username = None
145                 self._password = None
146
147         def is_logged_in(self):
148                 if self._username is None and self._password is None:
149                         _moduleLogger.info("Hasn't even attempted to login yet")
150                         return False
151                 elif self._backend.is_authed():
152                         return True
153                 else:
154                         try:
155                                 loggedIn = self._backend.login(self._username, self._password)
156                         except RuntimeError, e:
157                                 _moduleLogger.exception("Re-authenticating and erroring")
158                                 loggedIn = False
159                         if loggedIn:
160                                 return True
161                         else:
162                                 _moduleLogger.info("Login failed")
163                                 self.logout()
164                                 return False
165
166         def set_dnd(self, doNotDisturb):
167                 self._backend.set_dnd(doNotDisturb)
168                 self._cachedIsDnd = doNotDisturb
169
170         def is_dnd(self):
171                 # To throttle checking with the server, use a 30s cache
172                 newTime = time.time()
173                 if self._lastDndCheck + 30 < newTime:
174                         self._lasDndCheck = newTime
175                         self._cachedIsDnd = self._backend.is_dnd()
176                 return self._cachedIsDnd
177
178         @property
179         def backend(self):
180                 """
181                 Login enforcing backend
182                 """
183                 assert self.is_logged_in(), "User not logged in"
184                 return self._backend
185
186         @property
187         def addressbook(self):
188                 return self._addressbook
189
190         @property
191         def texts(self):
192                 return self._texts
193
194         @property
195         def voicemails(self):
196                 return self._voicemails
197
198         @property
199         def stateMachine(self):
200                 return self._masterStateMachine
201
202         @property
203         def addressbookStateMachine(self):
204                 return self._addressbookStateMachine
205
206         @property
207         def voicemailsStateMachine(self):
208                 return self._voicemailsStateMachine
209
210         @property
211         def textsStateMachine(self):
212                 return self._textsStateMachine