c040de416c9955f93e61e3bb402fc3ef60ad7873
[snippets/.git] / barn-growl / barn-growl.py
1 #!/usr/bin/env python
2
3 """
4 Subscribes to zephyr via tzc and sends messages to notification drivers (growl or libnotify).
5 """
6
7 import sexpr
8 import os
9 import subprocess
10 import fcntl
11 import select
12 import sys
13 from abstfilter import AbstractConsumer
14 import optparse
15
16 class Notifier(AbstractConsumer):
17     def __init__(self, usegrowl, usenotify, useprint):
18         self.usegrowl = usegrowl
19         self.usenotify = usenotify
20         if usenotify:
21             import pynotify
22             pynotify.init("Zephyr")
23             self.pings = {}
24             self.pynotify = pynotify
25         self.useprint = useprint
26         return
27     def feed(self, s):
28         if s is None or type(s) is type(''): return
29         d = dict([(ss[0], len(ss) > 2 and ss[2] or None) for ss in s])
30         if d['tzcspew'] == 'message':
31             zclass = d['class'].lower()
32             zinstance = d['instance'].lower()
33             zop = d['opcode'].lower()
34             zsender = d['sender'].lower()
35             zauth = d['auth'].lower() == 'yes'
36             ztime = ':'.join(d['time'].split(' ')[3].split(':')[0:2])
37             zmessage = d['message']
38             idtuple = (zclass, zinstance, zsender, ztime)
39             id = '%s/\n%s/\n%s\n %s' % idtuple
40             if zop == 'ping':
41                 header = '%s (%s)' % (id, zsender)
42                 message = '...'
43             elif zop == 'nil':
44                 header = '%s (%s)' % (id, len(zmessage) > 0 and zmessage[0] or zsender)
45                 message = '%s' % (len(zmessage) > 1 and zmessage[1] or '')
46             else:
47                 return
48             if self.useprint:
49                 print (id, header)
50                 print message
51             if self.usegrowl:
52                 growlnotify = ['growlnotify', '-a', 'MacZephyr', '-n', 'zephyr', '-d', id, '-t', header]
53                 g = subprocess.Popen(growlnotify, stdin=subprocess.PIPE)
54                 g.stdin.write(message)
55                 g.stdin.close()
56             if self.usenotify:
57                 if idtuple in self.pings:
58                     self.pings[idtuple].update(header, message)
59                     self.pings[idtuple].show()
60                 else:
61                     n = self.pynotify.Notification(header, message)
62                     n.show()
63                     if zop == 'ping':
64                         self.pings[idtuple] = n
65                 self.pings = dict(filter(lambda ((c, i, s, time), v): time == idtuple[3], self.pings.items()))
66     def close(self):
67         return
68
69 def main(argv):
70     parser = optparse.OptionParser(usage = '%prog [-s "username@machine"] (--growl | --notify | --print)',
71             description = __doc__.strip())
72     parser.add_option('-s', '--ssh',
73             type = 'string',
74             default = None,
75             dest = 'ssh',
76             help = 'optional remote host to run tzc')
77     parser.add_option('-g', '--growl',
78             action = 'store_true',
79             default = False,
80             dest = 'growl',
81             help = 'use growlnotify for output')
82     parser.add_option('-n', '--notify',
83             action = 'store_true',
84             default = False,
85             dest = 'notify',
86             help = 'use notify-send for output')
87     parser.add_option('-p', '--print',
88             action = 'store_true',
89             default = False,
90             dest = 'useprint',
91             help = 'use stdout for output')
92     opts, args = parser.parse_args()
93
94     usegrowl = opts.growl
95     usenotify = opts.notify
96     useprint = opts.useprint
97     if not usegrowl and not usenotify and not useprint:
98         parser.print_help(sys.stderr)
99         return 1
100     ssh = opts.ssh
101
102     if ssh is None:
103         retval = subprocess.call(['which', 'tzc'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
104         if retval:
105             print 'tzc not in path.  Please add -s username@machine to specify remote host.'
106             return 1
107
108     if ssh is not None:
109         command = "ssh -K %s 'tzc -si'" % ssh
110     else:
111         command = "tzc -si"
112     p = os.popen(command)
113     r = sexpr.SExprReader(Notifier(usegrowl, usenotify, useprint))
114
115     flags = fcntl.fcntl(p, fcntl.F_GETFL)
116     fcntl.fcntl(p, fcntl.F_SETFL, flags | os.O_NONBLOCK)
117
118     try:
119         while 1:
120             [i,o,e] = select.select([p], [], [], 5)
121             if i: s = p.read(1024)
122             else: s = ''
123
124             if s != '':
125                 r.feed(s)
126     except KeyboardInterrupt:
127         pass
128     return 0
129
130 if __name__ == "__main__":
131     sys.exit(main(sys.argv))