### # Copyright (c) 2008/2009, Lakin Wecker and Robert Brewer # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions, and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions, and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the author of this software nor the name of # contributors to this software may be used to endorse or promote products # derived from this software without specific prior written consent. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. ### import supybot.log as log import supybot.utils as utils from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks from supybot.plugins.Insult import plugin as insult_p from supybot.ircutils import isChannel import urllib2, cookielib, sys from urlparse import urljoin import BeautifulSoup as BS from BeautifulSoup import BeautifulSoup, NavigableString class SupyTrac(callbacks.PluginRegexp): threaded = True regexps = [ 'ticketSnarfer', 'revisionSnarfer', 'execSnarfer', 'wikiSnarfer', ] def __init__(self, irc): self.__parent = super(SupyTrac, self) self.__parent.__init__(irc) self.cookieJar = cookielib.CookieJar() self.urlOpener = urllib2.build_opener( urllib2.HTTPCookieProcessor(self.cookieJar)) urllib2.install_opener(self.urlOpener) def getSoupFromTracUrl(self, url): flob = urllib2.urlopen(url) return BeautifulSoup(flob.read()) def public_uri(self, channel, uri): """Return the full public URI for the given relative URI atoms.""" base = self.registryValue('base', channel) result = urljoin(base, uri) log.info(result) return result def private_uri(self, channel, uri): """Return the full private URI for the given relative URI atoms.""" base = self.registryValue('private-base', channel) if not base: base = self.registryValue('base', channel) result = urljoin(base, uri) log.info(result) return result def wikiSnarfer(self, irc, msg, match): r"(?:([a-zA-Z0-9\/_]+):)?wiki:([A-za-z0-9\/_]+)" channel = msg.args[0] if not irc.isChannel(channel): return g = list(match.groups()) wikiPage = g.pop() if g: #TODO: allow a channel to link to multiple sites site = g[0] url = self.public_uri(channel, '/'.join(('wiki', wikiPage))) irc.reply(url, prefixNick=False) wikiSnarfer = urlSnarfer(wikiSnarfer) def ticketSnarfer(self, irc, msg, match): r"(?:([a-zA-Z0-9\/_]+):)?(?:#|\bticket[\s:])(\d{2,})\b" channel = msg.args[0] if not irc.isChannel(channel): return g = list(match.groups()) ticketNumber = g.pop() if g: #TODO: allow a channel to link to multiple sites site = g[0] ticketURL = self.private_uri(channel, '/'.join(('ticket', ticketNumber))) soup = self.getSoupFromTracUrl(ticketURL) title = soup.findAll(attrs={'class': 'summary'})[0].string tag = soup.findAll(attrs={'class': 'status'})[0] status = tag.findChild('strong') status = status.string ticketURL = self.public_uri(channel, '/'.join(('ticket', ticketNumber))) irc.reply('%s - %s - %s' % (ticketURL, status, title), prefixNick=False) ticketSnarfer = urlSnarfer(ticketSnarfer) def revisionSnarfer(self, irc, msg, match): r"(?:([a-zA-Z0-9\/_]+):)?(?:\[\d{4,}\]|\b(?:r|changeset[\s:])(\d{3,})\b)" channel = msg.args[0] if not irc.isChannel(channel): return g = list(match.groups()) revisionNumber = g.pop() if g: #TODO: allow a channel to link to multiple sites site = g[0] changesetURL = self.private_uri(channel, '/'.join(('changeset', revisionNumber))) soup = self.getSoupFromTracUrl(changesetURL) p = soup.findAll('dd', attrs={'class': 'message'})[0] message_tags = p.findAll(text=lambda text: isinstance(text, NavigableString)) message = ''.join([tag for tag in message_tags]).replace('\n', ' ') author = soup.findAll('dd', attrs={'class': 'author'})[0].string changesetURL = self.public_uri(channel, '/'.join(('changeset', revisionNumber))) irc.reply('%s - %s - %s' % (changesetURL, author, message), prefixNick=False) revisionSnarfer = urlSnarfer(revisionSnarfer) def execSnarfer(self, irc, msg, match): r"(^>>> exec\()" irc.reply('TODO!') execSnarfer = urlSnarfer(execSnarfer) Class = SupyTrac # vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: