Put the bot into a 60-second loop.
[jira-zephyrbot.git] / jirabot.py
blobc2fbf06df2b5c3d105dc34456307fedcd96f420d
1 #!/usr/bin/python
2 import cStringIO
3 import calendar
4 import feedparser
5 import formatter
6 import htmllib
7 import mechanize
8 import os
9 import random
10 import string
11 import time
12 import traceback
13 import urlparse
14 import zephyr
16 zephyr_sender = 'jira'
17 zephyr_class = 'andersk-test'
18 time_file = 'jirabot.time'
20 def jira_init():
21 b = mechanize.Browser()
22 b.set_handle_robots(False)
23 b.add_client_certificate("https://idp.mit.edu:9443", "cert.pem", "cert.pem")
24 b.addheaders = [("Accept-Language", "en-us,en;q=0.5"),]
25 return b
27 def jira_login(b):
28 b.open("https://jira.mit.edu/jira/secure/Dashboard.jspa")
29 try:
30 b.follow_link(text="MIT Touchstone")
31 except mechanize.LinkNotFoundError:
32 return
33 if (urlparse.urlparse(b.geturl())[1] == "jira.mit.edu"):
34 return
35 b.select_form("wayfForm1")
36 b.submit()
37 b.select_form(predicate=lambda f: any(c.name == 'login_certificate'
38 for c in f.controls))
39 b.submit()
40 b.select_form(nr=0)
41 b.submit()
43 def feed_to_zephyrs(thing, rss, parse):
44 zephyrs = []
45 try:
46 feed = feedparser.parse(rss)
47 for e in feed.entries:
48 global old_time, new_time
49 t = int(calendar.timegm(e.date_parsed))
50 if t <= old_time:
51 continue
52 if t > new_time:
53 new_time = t
54 try:
55 z = parse(e)
56 except:
57 z = zerror("Error parsing " + thing + ":\n" + e.id + "\n" + traceback.format_exc())
58 zephyrs.append((t, z))
59 except:
60 zephyrs.append((0, zerror("Error parsing " + thing + "s feed:\n" + traceback.format_exc())))
61 return zephyrs
63 def parse_issue(e):
64 issue = urlparse.urlparse(e.id)[2].rsplit('/', 1)[1]
65 url = e.id
66 msg = e.id + "\nThis issue was updated."
68 return zephyr.ZNotice(
69 sender=zephyr_sender,
70 auth=False,
71 cls=zephyr_class,
72 instance=issue,
73 fields=[e.title, msg],
76 def parse_comment(e):
77 url = urlparse.urlunsplit(urlparse.urlparse(e.id)[0:3] + (None,None))
78 issue = url.rsplit('/', 1)[1]
80 s = cStringIO.StringIO()
81 parser = htmllib.HTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(s)))
82 parser.feed(e.summary.rsplit('<table>', 1)[0])
83 parser.close()
84 s.seek(0)
85 comment = s.read()
87 msg = e.author + " added a comment:\n" + comment.rstrip()
89 return zephyr.ZNotice(
90 sender=zephyr_sender,
91 auth=False,
92 cls=zephyr_class,
93 instance=issue,
94 fields=[e.title, msg],
97 def zerror(msg):
98 return zephyr.ZNotice(
99 sender=zephyr_sender,
100 auth=False,
101 cls=zephyr_class,
102 instance='jira-error',
103 fields=['Jira bot error', msg]
106 b = jira_init()
107 zephyr.init()
109 while True:
110 jira_login(b)
111 b.open("https://jira.mit.edu/jira/sr/jira.issueviews:searchrequest-rss/temp/SearchRequest.xml?&pid=10185&updated%3Aprevious=-1w&sorter/field=updated&sorter/order=DESC&tempMax=1000")
112 issues_rss = b.response().read()
113 b.open("https://jira.mit.edu/jira/sr/jira.issueviews:searchrequest-comments-rss/temp/SearchRequest.xml?&pid=10185&updated%3Aprevious=-1w&sorter/field=updated&sorter/order=DESC&tempMax=1000")
114 comments_rss = b.response().read()
116 time_file_new = time_file + '.' + ''.join(random.sample(string.letters, 8))
118 try:
119 os.rename(time_file, time_file_new)
120 except OSError:
121 exit()
123 old_time = int(open(time_file_new).read())
124 new_time = old_time
126 zephyrs = (feed_to_zephyrs('issue', issues_rss, parse_issue) +
127 feed_to_zephyrs('comment', comments_rss, parse_comment))
129 open(time_file_new, 'w').write(str(new_time))
131 os.rename(time_file_new, time_file)
133 zephyrs.sort(key=lambda tz: tz[0])
134 for (t, z) in zephyrs:
135 z.send()
136 time.sleep(60)