plugins/Sourceforge.py: lots of cleanups to the code. Also changed how

togglesnarfer works.
test/test_Sourceforge.py: updated to follow the plugin changes
This commit is contained in:
James Vega 2003-10-27 20:24:32 +00:00
parent 19f50ea8f2
commit fbb37025dc
2 changed files with 161 additions and 144 deletions

View File

@ -54,12 +54,24 @@ def configure(onStart, afterConnect, advanced):
# commands you would like to be run when the bot has finished connecting. # commands you would like to be run when the bot has finished connecting.
from questions import expect, anything, something, yn from questions import expect, anything, something, yn
onStart.append('load Sourceforge') onStart.append('load Sourceforge')
if advanced:
print 'The Sourceforge plugin has the functionality to watch for URLs' print 'The Sourceforge plugin has the functionality to watch for URLs'
print 'that match a specific pattern (we call this a snarfer). When' print 'that match a specific pattern (we call this a snarfer). When'
print 'supybot sees such a URL, he will parse the web page for information' print 'supybot sees such a URL, he will parse the web page for'
print 'and reply with the results.\n' print 'information and reply with the results.\n'
if yn('Do you want the Sourceforge snarfer enabled by default?') =='n': if yn('Do you want the Sourceforge snarfer enabled by default?') =='n':
onStart.append('Sourceforge togglesnarfer') onStart.append('Sourceforge togglesnarfer tracker off')
print 'The bugs and rfes commands of the Sourceforge plugin can be set'
print 'to query a default project when no project is specified. If this'
print 'project is not set, calling either of those commands will display'
print 'the associated help. With the default project set, calling'
print 'bugs/rfes with no arguments will find the most recent bugs/rfes'
print 'for the default project.\n'
if yn('Do you want to specify a default project?') == 'y':
project = anything('Project name:')
if project:
onStart.append('Sourceforge setdefault %s' % project)
example = utils.wrapLines(""" example = utils.wrapLines("""
<@jamessan|work> @bugs <@jamessan|work> @bugs
@ -84,36 +96,23 @@ class Sourceforge(callbacks.PrivmsgCommandAndRegexp):
regexps = ['sfSnarfer'] regexps = ['sfSnarfer']
_reopts = re.I _reopts = re.I
_infoRe = re.compile(r'<td nowrap>(\d+)</td><td><a href='\
_infoRe = re.compile(r'<td nowrap>(\d+)</td><td><a href="([^"]+)">'\ '"([^"]+)">([^<]+)</a>', _reopts)
'([^<]+)</a>', _reopts) _hrefOpts = '&set=custom&_assigned_to=0&_status=1&_category'\
_hrefOpts = '&set=custom&_assigned_to=0&_status=1&_category=100&'\ '=100&_group=100&order=artifact_id&sort=DESC'
'_group=100&order=artifact_id&sort=DESC' _resolution = re.compile(r'<b>Resolution:</b> <a.+?<br>(.+?)</td>',_reopts)
_resolution = re.compile(r'<b>Resolution:</b> <a.+?<br>(.+?)</td>',
_reopts)
_getRes = lambda self, s: '%s: %s' % (ircutils.bold('Resolution'),
self._resolution.search(s).group(1))
_assigned = re.compile(r'<b>Assigned To:</b> <a.+?<br>(.+?)</td>', _reopts) _assigned = re.compile(r'<b>Assigned To:</b> <a.+?<br>(.+?)</td>', _reopts)
_getAssign = lambda self, s: '%s: %s' % (ircutils.bold('Assigned to'),
self._assigned.search(s).group(1))
_submitted = re.compile(r'<b>Submitted By:</b><br>([^<]+)</td>', _reopts) _submitted = re.compile(r'<b>Submitted By:</b><br>([^<]+)</td>', _reopts)
_getSubmit = lambda self, s: '%s: %s' % (ircutils.bold('Submmited by'),
self._submitted.search(s).group(1))
_priority = re.compile(r'<b>Priority:</b> <a.+?<br>(.+?)</td>', _reopts) _priority = re.compile(r'<b>Priority:</b> <a.+?<br>(.+?)</td>', _reopts)
_getPri = lambda self, s: '%s: %s' % (ircutils.bold('Priority'),
self._priority.search(s).group(1))
_status = re.compile(r'<b>Status:</b> <a.+?<br>(.+?)</td>', _reopts) _status = re.compile(r'<b>Status:</b> <a.+?<br>(.+?)</td>', _reopts)
_getStatus = lambda self, s: '%s: %s' % (ircutils.bold('Status'), _res ={'Resolution':_resolution,'Assigned to':_assigned,
self._status.search(s).group(1)) 'Submitted by':_submitted, 'Priority':_priority,
'Status':_status}
def __init__(self): def __init__(self):
callbacks.PrivmsgCommandAndRegexp.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self)
self.snarfer = True self.snarfers = {'tracker' : True}
self.project = None
def _formatResp(self, num, text): def _formatResp(self, num, text):
""" """
@ -133,18 +132,99 @@ class Sourceforge(callbacks.PrivmsgCommandAndRegexp):
matches.append((item[0], utils.htmlToText(item[2]))) matches.append((item[0], utils.htmlToText(item[2])))
return matches return matches
def togglesnarfer(self, irc, msg, args): def setdefault(self, irc, msg, args):
"""takes no argument """<project>
Disables the snarfer that responds to all Sourceforge Tracker links Sets the default project to be used with bugs and rfes
""" """
self.snarfer = not self.snarfer project = privmsgs.getArgs(args)
if self.snarfer: self.project = project
irc.reply(msg, '%s (Snarfer is enabled)' % conf.replySuccess) irc.reply(msg, conf.replySuccess)
setdefault = privmsgs.checkCapability(setdefault, 'admin')
def _toggleHelper(self, irc, msg, state, snarfer):
if not state:
self.snarfers[snarfer] = not self.snarfers[snarfer]
elif state in self._enable:
self.snarfers[snarfer] = True
elif state in self._disable:
self.snarfers[snarfer] = False
resp = []
for k in self.snarfers:
if self.snarfers[k]:
resp.append('%s%s: On' % (k[0].upper(), k[1:]))
else: else:
irc.reply(msg, '%s (Snarfer is disabled)' % conf.replySuccess) resp.append('%s%s: Off' % (k[0].upper(), k[1:]))
irc.reply(msg, '%s (%s)' % (conf.replySuccess, '; '.join(resp)))
_enable = ('on', 'enable')
_disable = ('off', 'disable')
def togglesnarfer(self, irc, msg, args):
"""<tracker> [<on|off>]
Toggles the snarfer that responds to Sourceforge Tracker links. If
nothing is specified, all snarfers will have their states
toggled (on -> off, off -> on). If only a state is specified, all
snarfers will have their state set to the specified state. If a
specific snarfer is specified, the changes will apply only to that
snarfer.
"""
(snarfer, state) = privmsgs.getArgs(args, optional=1)
snarfer = snarfer.lower()
state = state.lower()
if snarfer not in self.snarfers:
raise callbacks.ArgumentError
if state and state not in self._enable and state not in self._disable:
raise callbacks.ArgumentError
self._toggleHelper(irc, msg, state, snarfer)
togglesnarfer=privmsgs.checkCapability(togglesnarfer, 'admin') togglesnarfer=privmsgs.checkCapability(togglesnarfer, 'admin')
def _getTrackerInfo(self, irc, msg, url, regex, num):
try:
fd = urllib2.urlopen(url)
text = fd.read()
fd.close()
m = regex.search(text)
if m is None:
irc.reply(msg, 'Can\'t find the proper Tracker link.')
return
else:
url = 'http://sourceforge.net%s%s' %\
(utils.htmlToText(m.group(1)), self._hrefOpts)
except ValueError, e:
irc.error(msg, str(e))
except urllib2.HTTPError, e:
irc.error(msg, e.msg())
except Exception, e:
irc.error(msg, debug.exnToString(e))
try:
fd = urllib2.urlopen(url)
text = fd.read()
fd.close()
resp = []
if num != '':
head = '%s <http://sourceforge.net%s>'
for match in self._formatResp(num, text):
resp.append(head % match)
if resp:
irc.reply(msg, resp[0])
return
else:
head = '#%s: %s'
for entry in self._formatResp(num, text):
resp.append(head % entry)
if resp:
if len(resp) > 10:
resp = map(lambda s: utils.ellipsisify(s, 50), resp)
irc.reply(msg, '%s' % utils.commaAndify(resp))
return
irc.reply(msg, 'No Trackers were found.')
except ValueError, e:
irc.error(msg, str(e))
except Exception, e:
irc.error(msg, debug.exnToString(e))
_bugLink = re.compile(r'"([^"]+)">Bugs') _bugLink = re.compile(r'"([^"]+)">Bugs')
def bugs(self, irc, msg, args): def bugs(self, irc, msg, args):
"""[<project> [<num>]] """[<project> [<num>]]
@ -155,52 +235,18 @@ class Sourceforge(callbacks.PrivmsgCommandAndRegexp):
""" """
(project, bugnum) = privmsgs.getArgs(args, needed=0, optional=2) (project, bugnum) = privmsgs.getArgs(args, needed=0, optional=2)
if not project: if not project:
project = 'supybot' if self.project is None:
raise callbacks.ArgumentError
else:
project = self.project
elif not bugnum:
try:
bugnum = int(project)
project = self.project
except ValueError:
pass
url = 'http://sourceforge.net/projects/%s' % project url = 'http://sourceforge.net/projects/%s' % project
try: self._getTrackerInfo(irc, msg, url, self._bugLink, bugnum)
fd = urllib2.urlopen(url)
text = fd.read()
fd.close()
m = self._bugLink.search(text)
if m is None:
irc.reply(msg, 'Can\'t find the "Bugs" link.')
return
else:
url = 'http://sourceforge.net%s%s' %\
(utils.htmlToText(m.group(1)), self._hrefOpts)
except ValueError, e:
irc.error(msg, str(e))
except urllib2.HTTPError, e:
irc.error(msg, e.msg())
except Exception, e:
irc.error(msg, debug.exnToString(e))
try:
fd = urllib2.urlopen(url)
text = fd.read()
fd.close()
resp = []
if bugnum != '':
head = '%s <http://sourceforge.net%s>'
for bug in self._formatResp(bugnum, text):
resp.append(head % bug)
if resp:
irc.reply(msg, resp[0])
return
else:
head = 'Bug #%s: %s'
for bug in self._formatResp(bugnum, text):
resp.append(head % bug)
if resp:
if len(resp) > 10:
resp = map(lambda s: utils.ellipsisify(s, 50), resp)
irc.reply(msg, '%s' % utils.commaAndify(resp))
return
irc.reply(msg, 'No bugs were found.')
except ValueError, e:
irc.error(msg, str(e))
except Exception, e:
irc.error(msg, debug.exnToString(e))
_rfeLink = re.compile(r'"([^"]+)">RFE') _rfeLink = re.compile(r'"([^"]+)">RFE')
def rfes(self, irc, msg, args): def rfes(self, irc, msg, args):
@ -212,72 +258,38 @@ class Sourceforge(callbacks.PrivmsgCommandAndRegexp):
""" """
(project, rfenum) = privmsgs.getArgs(args, needed=0, optional=2) (project, rfenum) = privmsgs.getArgs(args, needed=0, optional=2)
if not project: if not project:
project = 'supybot' if self.project is None:
raise callbacks.ArgumentError
else:
project = self.project
elif not rfenum:
try:
rfenum = int(project)
project = self.project
except ValueError:
pass
url = 'http://sourceforge.net/projects/%s' % project url = 'http://sourceforge.net/projects/%s' % project
try: self._getTrackerInfo(irc, msg, url, self._rfeLink, rfenum)
fd = urllib2.urlopen(url)
text = fd.read()
fd.close()
m = self._rfeLink.search(text)
if m is None:
irc.reply(msg, 'Can\'t find the "RFE" link.')
return
else:
url = 'http://sourceforge.net%s%s' %\
(utils.htmlToText(m.group(1)), self._hrefOpts)
except ValueError, e:
irc.error(msg, str(e))
except urllib2.HTTPError, e:
irc.error(msg, e.msg())
except Exception, e:
irc.error(msg, debug.exnToString(e))
try:
fd = urllib2.urlopen(url)
text = fd.read()
fd.close()
resp = []
if rfenum != '':
head = '%s <http://sourceforge.net%s>'
for rfe in self._formatResp(rfenum, text):
resp.append(head % rfe)
if resp:
irc.reply(msg, resp[0])
return
else:
head = 'RFE #%s: %s'
for rfe in self._formatResp(rfenum, text):
resp.append(head % rfe)
if resp:
if len(resp) > 10:
resp = map(lambda s: utils.ellipsisify(s, 50), resp)
irc.reply(msg, '%s' % utils.commaAndify(resp))
return
irc.reply(msg, 'No rfes were found.')
except ValueError, e:
irc.error(msg, str(e))
except Exception, e:
irc.error(msg, debug.exnToString(e))
_getSnarferInfo = lambda self, k, v, s: '%s: %s' % (ircutils.bold(k),
v.search(s).group(1))
_sfTitle = re.compile(r'Detail:(\d+) - ([^<]+)</title>', re.I) _sfTitle = re.compile(r'Detail:(\d+) - ([^<]+)</title>', re.I)
_linkType = re.compile(r'(\w+ \w+|\w+): Tracker Detailed View', re.I) _linkType = re.compile(r'(\w+ \w+|\w+): Tracker Detailed View', re.I)
def sfSnarfer(self, irc, msg, match): def sfSnarfer(self, irc, msg, match):
r"https?://(?:www\.)?(?:sourceforge|sf)\.net/tracker/(?:index\.php)?\?(?:&?func=detail|&?aid=\d+|&?group_id=\d+|&?atid=\d+){4}" r"https?://(?:www\.)?(?:sourceforge|sf)\.net/tracker/(?:index\.php)?\?(?:&?func=detail|&?aid=\d+|&?group_id=\d+|&?atid=\d+){4}"
if not self.snarfer: if not self.snarfers['tracker']:
return return
url = match.group(0) url = match.group(0)
fd = urllib2.urlopen(url) fd = urllib2.urlopen(url)
s = fd.read() s = fd.read()
fd.close() fd.close()
searches = (self._getSubmit, self._getStatus, self._getRes,
self._getPri, self._getAssign)
try: try:
(num, desc) = self._sfTitle.search(s).groups() (num, desc) = self._sfTitle.search(s).groups()
resp = [desc] resp = [desc]
linktype = self._linkType.search(s).group(1) linktype = self._linkType.search(s).group(1)
for i in searches: for k,v in self._res.iteritems():
try: try:
resp.append('%s' % i(s)) resp.append(self._getSnarferInfo(k, v, s))
except AttributeError: except AttributeError:
pass pass
linktype = utils.depluralize(linktype) linktype = utils.depluralize(linktype)

View File

@ -36,23 +36,29 @@ from test import *
class SourceforgeTest(PluginTestCase, PluginDocumentation): class SourceforgeTest(PluginTestCase, PluginDocumentation):
plugins = ('Sourceforge',) plugins = ('Sourceforge',)
def testBugs(self): def testBugs(self):
self.assertNotError('bugs') self.assertHelp('bugs')
self.assertResponse('bugs alkjfi83fa8', 'Can\'t find the "Bugs" link.') self.assertResponse('bugs alkjfi83fa8', 'Can\'t find the proper '\
'Tracker link.')
self.assertNotError('bugs gaim') self.assertNotError('bugs gaim')
m = self.getMsg('bugs gaim') m = self.getMsg('bugs gaim')
n = re.search('#(\d+)', m.args[1]).group(1) n = re.search('#(\d+)', m.args[1]).group(1)
self.assertNotError('bugs gaim %s' % n) self.assertNotError('bugs gaim %s' % n)
def testRfes(self): def testRfes(self):
self.assertNotError('rfes') self.assertHelp('rfes')
self.assertResponse('rfes alkjfi83hfa8', 'Can\'t find the "RFE" link.') self.assertResponse('rfes alkjfi83hfa8', 'Can\'t find the proper '\
'Tracker link.')
self.assertNotError('rfes gaim') self.assertNotError('rfes gaim')
m = self.getMsg('rfes gaim') m = self.getMsg('rfes gaim')
n = re.search('#(\d+)', m.args[1]).group(1) n = re.search('#(\d+)', m.args[1]).group(1)
self.assertNotError('rfes gaim %s' % n) self.assertNotError('rfes gaim %s' % n)
def testSetdefault(self):
self.assertNotError('setdefault supybot')
self.assertNotError('rfes')
def testSnarfer(self): def testSnarfer(self):
s = r';.*Status.*: \w+;' s = r'.*Status.*: \w+'
self.assertRegexp('http://sourceforge.net/tracker/index.php?'\ self.assertRegexp('http://sourceforge.net/tracker/index.php?'\
'func=detail&aid=589953&group_id=58965&atid=489447', s) 'func=detail&aid=589953&group_id=58965&atid=489447', s)
self.assertRegexp('http://sourceforge.net/tracker/index.php?'\ self.assertRegexp('http://sourceforge.net/tracker/index.php?'\
@ -83,16 +89,15 @@ class SourceforgeTest(PluginTestCase, PluginDocumentation):
'group_id=58965&atid=489447') 'group_id=58965&atid=489447')
def testDisablesfsnarfer(self): def testDisablesfsnarfer(self):
s = r'.*Status.*: \w+'
self.assertRegexp('http://sourceforge.net/tracker/index.php?'\ self.assertRegexp('http://sourceforge.net/tracker/index.php?'\
'func=detail&aid=540223&group_id=235&atid=300235', 'func=detail&aid=540223&group_id=235&atid=300235', s)
r';.*Status.*: \w+;') self.assertNotError('togglesnarfer tracker off')
self.assertNotError('togglesnarfer')
self.assertNoResponse('http://sourceforge.net/tracker/index.php?'\ self.assertNoResponse('http://sourceforge.net/tracker/index.php?'\
'func=detail&aid=540223&group_id=235&atid=300235') 'func=detail&aid=540223&group_id=235&atid=300235')
self.assertNotError('togglesnarfer') self.assertNotError('togglesnarfer tracker on')
self.assertRegexp('http://sourceforge.net/tracker/index.php?'\ self.assertRegexp('http://sourceforge.net/tracker/index.php?'\
'func=detail&aid=540223&group_id=235&atid=300235', 'func=detail&aid=540223&group_id=235&atid=300235', s)
r';.*Status.*: \w+;')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: