Improving the notifier
[gc-dialer] / src / alarm_notify.py
1 #!/usr/bin/env python
2
3 import os
4 import filecmp
5 import ConfigParser
6 import pprint
7 import logging
8
9 import constants
10 from backends.gvoice import gvoice
11
12
13 def get_missed(backend):
14         missedPage = backend._browser.download(backend._XML_MISSED_URL)
15         missedJson = backend._grab_json(missedPage)
16         return missedJson
17
18
19 def get_voicemail(backend):
20         voicemailPage = backend._browser.download(backend._XML_VOICEMAIL_URL)
21         voicemailJson = backend._grab_json(voicemailPage)
22         return voicemailJson
23
24
25 def get_sms(backend):
26         smsPage = backend._browser.download(backend._XML_SMS_URL)
27         smsJson = backend._grab_json(smsPage)
28         return smsJson
29
30
31 def remove_reltime(data):
32         for messageData in data["messages"].itervalues():
33                 for badPart in [
34                         "relTime",
35                         "relativeStartTime",
36                         "time",
37                         "star",
38                         "isArchived",
39                         "isRead",
40                         "isSpam",
41                         "isTrash",
42                         "labels",
43                 ]:
44                         if badPart in messageData:
45                                 del messageData[badPart]
46         for globalBad in ["unreadCounts", "totalSize", "resultsPerPage"]:
47                 if globalBad in data:
48                         del data[globalBad]
49
50
51 def is_type_changed(backend, type, get_material):
52         jsonMaterial = get_material(backend)
53         unreadCount = jsonMaterial["unreadCounts"][type]
54
55         previousSnapshotPath = os.path.join(constants._data_path_, "snapshot_%s.old.json" % type)
56         currentSnapshotPath = os.path.join(constants._data_path_, "snapshot_%s.json" % type)
57
58         try:
59                 os.remove(previousSnapshotPath)
60         except OSError, e:
61                 # check if failed purely because the old file didn't exist, which is fine
62                 if e.errno != 2:
63                         raise
64         try:
65                 os.rename(currentSnapshotPath, previousSnapshotPath)
66                 previousExists = True
67         except OSError, e:
68                 # check if failed purely because the new old file didn't exist, which is fine
69                 if e.errno != 2:
70                         raise
71                 previousExists = False
72
73         remove_reltime(jsonMaterial)
74         textMaterial = pprint.pformat(jsonMaterial)
75         currentSnapshot = file(currentSnapshotPath, "w")
76         try:
77                 currentSnapshot.write(textMaterial)
78         finally:
79                 currentSnapshot.close()
80
81         if unreadCount == 0 or not previousExists:
82                 return False
83
84         seemEqual = filecmp.cmp(previousSnapshotPath, currentSnapshotPath)
85         return not seemEqual
86
87
88 def create_backend(config):
89         gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
90         backend = gvoice.GVoiceBackend(gvCookiePath)
91
92         loggedIn = False
93
94         if not loggedIn:
95                 loggedIn = backend.is_authed()
96
97         if not loggedIn:
98                 import base64
99                 try:
100                         blobs = (
101                                 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
102                                 for i in xrange(2)
103                         )
104                         creds = (
105                                 base64.b64decode(blob)
106                                 for blob in blobs
107                         )
108                         username, password = tuple(creds)
109                         loggedIn = backend.login(username, password)
110                 except ConfigParser.NoOptionError, e:
111                         pass
112                 except ConfigParser.NoSectionError, e:
113                         pass
114
115         assert loggedIn
116         return backend
117
118
119 def is_changed(config, backend):
120         try:
121                 notifyOnMissed = config.getboolean("2 - Account Info", "notifyOnMissed")
122                 notifyOnVoicemail = config.getboolean("2 - Account Info", "notifyOnVoicemail")
123                 notifyOnSms = config.getboolean("2 - Account Info", "notifyOnSms")
124         except ConfigParser.NoOptionError, e:
125                 notifyOnMissed = False
126                 notifyOnVoicemail = False
127                 notifyOnSms = False
128         except ConfigParser.NoSectionError, e:
129                 notifyOnMissed = False
130                 notifyOnVoicemail = False
131                 notifyOnSms = False
132         logging.debug(
133                 "Missed: %s, Voicemail: %s, SMS: %s" % (notifyOnMissed, notifyOnVoicemail, notifyOnSms)
134         )
135
136         notifySources = []
137         if notifyOnMissed:
138                 notifySources.append(("missed", get_missed))
139         if notifyOnVoicemail:
140                 notifySources.append(("voicemail", get_voicemail))
141         if notifyOnSms:
142                 notifySources.append(("sms", get_sms))
143
144         notifyUser = False
145         for type, get_material in notifySources:
146                 if is_type_changed(backend, type, get_material):
147                         notifyUser = True
148         return notifyUser
149
150
151 def notify_on_change():
152         config = ConfigParser.SafeConfigParser()
153         config.read(constants._user_settings_)
154         backend = create_backend(config)
155         notifyUser = is_changed(config, backend)
156
157         if notifyUser:
158                 logging.info("Changed")
159                 import led_handler
160                 led = led_handler.LedHandler()
161                 led.on()
162         else:
163                 logging.info("No Change")
164
165
166 if __name__ == "__main__":
167         logging.basicConfig(level=logging.DEBUG, filename=constants._notifier_logpath_)
168         logging.info("Notifier %s-%s" % (constants.__version__, constants.__build__))
169         logging.info("OS: %s" % (os.uname()[0], ))
170         logging.info("Kernel: %s (%s) for %s" % os.uname()[2:])
171         logging.info("Hostname: %s" % os.uname()[1])
172         try:
173                 notify_on_change()
174         except:
175                 logging.exception("Error")
176                 raise