From 139d186c8313e91ed9418830a2dfb7ee7ef2b5e9 Mon Sep 17 00:00:00 2001 From: Dennis Kaarsemaker Date: Thu, 1 Feb 2007 09:46:32 +0100 Subject: [PATCH] Bugtracker now is much more usable and contains documentation. Backwards incompatible though! --- Bugtracker/README.txt | 17 ++ Bugtracker/plugin.py | 79 +++++-- Encyclopedia/plugin.py | 522 ----------------------------------------- 3 files changed, 78 insertions(+), 540 deletions(-) delete mode 100644 Encyclopedia/plugin.py diff --git a/Bugtracker/README.txt b/Bugtracker/README.txt index a0a8da5..ca7b4a5 100644 --- a/Bugtracker/README.txt +++ b/Bugtracker/README.txt @@ -26,6 +26,7 @@ Bugtracker dialects (types) this plugin understands: * Trac (with not-too-buggered-up templates, it needs to do screenscraping) * Sourceforge (needs atid and group_id in the url!) * WikiForms (see bugs.gnewsense.org for an example) +* str.php from the CUPS project To request a bug report, use this syntax: @@ -71,3 +72,19 @@ tag_here/malone/NN/MMMM where NN is int(bugid/1000) and MMMM is the bugid. If your products already have many bugreports, consider doing some screenscraping with the malone searchpages and sed/awk :) + +A quick hack I use to get all launchpad bugids preseeded: + +cd /home/ubugtu/data/bugmail # This is my cachedir +cd launchpad # This is the tag +mkdir malone +product=launchpad +amount=2000 # 2000 is the amount of bugs, chck this on + # launchpad under all bugs ever reported +# Download a summary of all bugs +for x in `seq 0 75 $amount`; do + wget "https://bugs.launchpad.net/$product/+bugs?search=Search&field.status=Unconfirmed&field.status=Confirmed&field.status=In+Progress&field.status=Needs+Info&field.status=Fix+Committed&field.status=Fix+Released&field.status=Rejected&field.omit_dupes.used=&start=$x" -O $x; +done + grep -h =.amount * | sed -e 's/.*>\(.*\)<.*/\1/' | awk '{print "malone/" int($1/1000)}' | sort -u | xargs mkdir -p + grep -h =.amount * | sed -e 's/.*>\(.*\)<.*/\1/' | awk '{print "malone/" int($1/1000) "/" $1}' | xargs -n100 touch + diff --git a/Bugtracker/plugin.py b/Bugtracker/plugin.py index 4fd0ffa..e0308cd 100644 --- a/Bugtracker/plugin.py +++ b/Bugtracker/plugin.py @@ -230,7 +230,7 @@ class Bugtracker(callbacks.PluginRegexp): irc.error(s % name) remove = wrap(remove, ['text']) - def rename(self, irc, msg, args, oldname, newname): + def rename(self, irc, msg, args, oldname, newname, newdesc): """ Rename the bugtracker associated with to @@ -238,8 +238,11 @@ class Bugtracker(callbacks.PluginRegexp): try: name = self.shorthand[oldname.lower()] group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False) - self.db[newname] = defined_bugtrackers[trackertype](name,group.url(),group.description()) - registerBugtracker(newname, group.url(), group.description(), group.trackertype()) + d = group.description() + if newdesc: + d = newdesc + self.db[newname] = defined_bugtrackers[group.trackertype()](name,group.url(),d) + registerBugtracker(newname, group.url(), d, group.trackertype()) del self.db[name] self.registryValue('bugtrackers').remove(name) self.shorthand = utils.abbrev(self.db.keys()) @@ -247,7 +250,7 @@ class Bugtracker(callbacks.PluginRegexp): except KeyError: s = self.registryValue('replyNoBugtracker', msg.args[0]) irc.error(s % name) - rename = wrap(rename, ['something','something']) + rename = wrap(rename, ['something','something', additional('text')]) def list(self, irc, msg, args, name): """[abbreviation] @@ -275,7 +278,7 @@ class Bugtracker(callbacks.PluginRegexp): list = wrap(list, [additional('text')]) def bugSnarfer(self, irc, msg, match): - r"""\b(?P(([a-z]+)?\s+bugs?|[a-z]+))\s+#?(?P\d+(?!\d*\.\d+)((,|\s*(and|en|et|und|ir))\s*#?\d+(?!\d*\.\d+))*)""" + r"""\b(?P(([a-z0-9]+)?\s+bugs?|[a-z]+))\s+#?(?P\d+(?!\d*\.\d+)((,|\s*(and|en|et|und|ir))\s*#?\d+(?!\d*\.\d+))*)""" if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]): return @@ -327,6 +330,8 @@ class Bugtracker(callbacks.PluginRegexp): continue try: report = self.get_bug(tracker,bugid,self.registryValue('showassignee', msg.args[0])) + except BugNotFoundError: + irc.error("%s bug %d could not be found" % (tracker.description, bugid)) except BugtrackerError, e: if 'private' in str(e): irc.reply("Bug %d on http://launchpad.net/bugs/%d is private" % (bugid, bugid)) @@ -339,7 +344,7 @@ class Bugtracker(callbacks.PluginRegexp): irc.reply(r, prefixNick=False) def turlSnarfer(self, irc, msg, match): - "(?Phttps?://.*?)(show_bug.cgi\?id=|bugreport.cgi\?bug=|(bugs|\+bug)/|/ticket/|tracker/.*aid=)(?P\d+)(?P&group_id=\d+&at_id=\d+)?" + r"(?Phttps?://\S*?)(show_bug.cgi\?id=|bugreport.cgi\?bug=|(bugs|\+bug)/|/ticket/|tracker/\S*aid=)(?P\d+)(?P&group_id=\d+&at_id=\d+)?" if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]): return try: @@ -447,6 +452,8 @@ class Bugzilla(IBugtracker): bug_n = zilladom.getElementsByTagName('bug')[0] if bug_n.hasAttribute('error'): errtxt = bug_n.getAttribute('error') + if errtxt == 'NotFound': + raise BugNotFoundError s = 'Error getting %s bug #%s: %s' % (self.description, id, errtxt) raise BugtrackerError, s try: @@ -458,7 +465,11 @@ class Bugzilla(IBugtracker): pass component = _getnodetxt(bug_n.getElementsByTagName('component')[0]) severity = _getnodetxt(bug_n.getElementsByTagName('bug_severity')[0]) - assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0]) + assignee = '(unavailable)' + try: + assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0]) + except: + pass except Exception, e: s = 'Could not parse XML returned by %s bugzilla: %s' % (self.description, e) raise BugtrackerError, s @@ -475,6 +486,8 @@ class Issuezilla(IBugtracker): raise BugtrackerError, s bug_n = zilladom.getElementsByTagName('issue')[0] if not (bug_n.getAttribute('status_code') == '200'): + if bug_n.getAttribute('status_message') == 'NotFound': + raise BugNotFoundError s = 'Error getting %s bug #%s: %s' % (self.description, id, bug_n.getAttribute('status_message')) raise BugtrackerError, s try: @@ -634,21 +647,25 @@ class Trac(IBugtracker): try: bugdata = utils.web.getUrl(url) except Exception, e: + # Hacketiehack + if 'HTTP Error 500' in str(e): + raise BugNotFoundError s = 'Could not parse data returned by %s: %s' % (self.description, e) raise BugtrackerError, s for l in bugdata.split("\n"): - if '

Ticket' in l: - severity = l[l.find('(')+1:l.find(')')] if 'class="summary"' in l: title = l[l.find('>')+1:l.find('')+8:l.find('')] + status = l[l.find('>(')+2:l.find(')')] if 'headers="h_component"' in l: package = l[l.find('>')+1:l.find('')+1:l.find('')+1:l.find('')+1:l.find('')+4:]) return [(id, package, title, severity, status, '', "%s/%05d" % (self.url, id))] +class Str(IBugtracker): + def get_bug(self, id): + def strip_tags(s): + while '<' in s and '>' in s: + s = str(s[:s.find('<')]) + str(s[s.find('>')+1:]) + return s + url = "%s?L%d" % (self.url, id) + try: + bugdata = utils.web.getUrl(url) + except Exception, e: + s = 'Could not parse data returned by %s: %s' % (self.description, e) + raise BugtrackerError, s + for l in bugdata.split("\n"): + l2 = l.lower() + if 'nowrap>priority:' in l2: + severity = 'Priority ' + l[l.find(' - ')+3:min(l.find(','),l.find(''))] + if '>application:' in l2: + package = l[l.find('')+4:l.find('')] + if 'nowrap>status:' in l2: + status = l[l.find(' - ')+3:l.find('')] + if 'nowrap>summary:' in l2: + title = l[l.find('')+4:l.find('')] + if 'nowrap>assigned to:' in l2: + assignee = strip_tags(l[l.find('')+4:l.find('')]) + if assignee == 'Unassigned': + assignee = 'nobody' + return [(id, package, title, severity, status, assignee, "%s/L%d" % (self.url, id))] + + sfre = re.compile(r""" .*?

\[.*?\]\s*(?P.*?)</h2> @@ -715,7 +763,6 @@ for k in v.keys(): if type(v[k]) == type(IBugtracker) and issubclass(v[k], IBugtracker) and not (v[k] == IBugtracker): defined_bugtrackers[k.lower()] = v[k] -# Let's add a few bugtrackers by default registerBugtracker('mozilla', 'http://bugzilla.mozilla.org', 'Mozilla', 'bugzilla') registerBugtracker('ubuntu', 'http://bugzilla.ubuntu.com', 'Ubuntu', 'bugzilla') registerBugtracker('gnome', 'http://bugzilla.gnome.org', 'Gnome', 'bugzilla') @@ -724,18 +771,14 @@ registerBugtracker('kde', 'http://bugs.kde.org', 'KDE', 'bugzilla') registerBugtracker('ximian', 'http://bugzilla.ximian.com', 'Ximian', 'bugzilla') registerBugtracker('freedesktop', 'http://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla') registerBugtracker('freedesktop2', 'http://bugs.freedesktop.org', 'Freedesktop', 'bugzilla') -# Given that there is only one, let's add it by default registerBugtracker('openoffice', 'http://openoffice.org/issues', 'OpenOffice.org', 'issuezilla') -# Given that there is only one, let's add it by default registerBugtracker('malone', 'https://launchpad.net/malone', 'Malone', 'malone') -# Given that there is only one, let's add it by default registerBugtracker('debian', 'http://bugs.debian.org', 'Debian', 'debbugs') -# Let's add a few bugtrackers by default registerBugtracker('trac', 'http://projects.edgewall.com/trac/ticket', 'Trac', 'trac') registerBugtracker('django', 'http://code.djangoproject.com/ticket', 'Django', 'trac') -# Let's add a few bugtrackers by default +registerBugtracker('cups', 'http://www.cups.org/str.php', 'CUPS', 'str') +registerBugtracker('gnewsense', 'http://bugs.gnewsense.org/Bugs', 'gNewSense', 'wikiforms') registerBugtracker('supybot', 'http://sourceforge.net/tracker/?group_id=58965&atid=489447', 'Supybot', 'sourceforge') -# Special one, do NOT disable/delete +# Don't delete this one registerBugtracker('sourceforge', 'http://sourceforge.net/tracker/', 'Sourceforge', 'sourceforge') - Class = Bugtracker diff --git a/Encyclopedia/plugin.py b/Encyclopedia/plugin.py deleted file mode 100644 index 23c5c07..0000000 --- a/Encyclopedia/plugin.py +++ /dev/null @@ -1,522 +0,0 @@ -### -# Copyright (c) 2006, Dennis Kaarsemaker -# All rights reserved. -# -# -### - -import supybot.utils as utils -from supybot.commands import * -import supybot.plugins as plugins -import supybot.ircutils as ircutils -import supybot.ircmsgs as ircmsgs -import supybot.callbacks as callbacks -import sqlite, datetime, time, apt_pkg, commands -import supybot.registry as registry -import supybot.ircdb as ircdb -from email import FeedParser -import re -import os -import fcntl -apt_pkg.init() - -fallback = ('ubuntu', '#ubuntu') -datadir = '/home/dennis/ubugtu/data/facts' - -def r(repo,section): - if 'seveas' in repo: - return 'Seveas' - if 'buntudot' in repo: - return 'buntudot' - if '/' in section: - return section[:section.find('/')] - return 'main' - -class Factoid: - def __init__(self, name, value, author, added, popularity): - self.name = name; self.value = value - self.author = author; self.added = added - self.popularity = popularity - -def get_factoid(db, name, channel): - cur = db.cursor() - cur.execute("SELECT name, value, author, added, popularity FROM facts WHERE name = %s", '%s-%s' % (name, channel)) - factoid = cur.fetchall() - if len(factoid): - f = factoid[0] - return Factoid(f[0].replace(channel,'')[:-1],f[1],f[2],f[3],f[4]) - cur.execute("SELECT name, value, author, added, popularity FROM facts WHERE name = %s", name) - factoid = cur.fetchall() - if len(factoid): - f = factoid[0] - return Factoid(f[0],f[1].replace('$chan',channel),f[2],f[3],f[4]) - return None - -def resolve_alias(db,factoid,channel,loop=0): - if loop >= 10: - return Factoid('','Error: infinite <alias> loop detected','','',0) - if factoid.value.lower().startswith('<alias>'): - new_factoid = get_factoid(db,factoid.value[7:].lower().strip(),channel) - if not new_factoid: - return Factoid('','Error: unresolvable <alias>','','',0) - else: - return resolve_alias(db, new_factoid, channel, loop+1) - else: - return factoid - -class Encyclopedia(callbacks.PluginRegexp): - """!factoid: show factoid""" - threaded = True - regexps = ['showfactoid', 'addfactoid', 'deletefactoid','info','find','editfactoid','searchfactoid','seen'] - - def __init__(self, irc): - callbacks.PluginRegexp.__init__(self, irc) - self.databases = {} - self.times = {} - self.seens = {} - - # Capability check - def _precheck(self, irc, msg, capability=None, timeout=None, withnick=False): - channel = msg.args[0].lower() - inchannel = channel.startswith('#') - excl = msg.args[1].startswith('!') - wn = msg.args[1].startswith('ubotu') - if inchannel and not (excl or (withnick and wn)): - return False - if msg.args[1].strip()[0] == '%': # FIXME: replywhenaddressed.chars oslt - return False - for c in irc.callbacks: - comm = msg.args[1].split()[0] - if c.isCommandMethod(comm) and not c.isDisabled(comm): - return False - if capability: - try: - _ = ircdb.users.getUser(msg.prefix) - if not ircdb.checkCapability(msg.prefix, capability): - raise KeyError, "Bogus error to trigger the log" - except KeyError: - irc.queueMsg(ircmsgs.privmsg('#ubuntu-ops', "In %s, %s said: %s" % (msg.args[0], msg.nick, msg.args[1]))) - irc.reply("Your edit request has been forwarded to #ubuntu-ops. Thank you for your attention to detail",private=True) - lfd = open('/var/www/bots.ubuntulinux.nl/botlogs/lock','a') - fcntl.lockf(lfd, fcntl.LOCK_EX) - fd = open('/var/www/bots.ubuntulinux.nl/botlogs/%s.log' % datetime.date.today().strftime('%Y-%m-%d'),'a') - fd.write("%s %-20s %-16s %s\n" % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),msg.args[0], msg.nick, msg.args[1])) - fd.close() - fcntl.lockf(lfd,fcntl.LOCK_UN) - lfd.close() - os.chmod('/var/www/bots.ubuntulinux.nl/botlogs/%s.log' % datetime.date.today().strftime('%Y-%m-%d'),0644) - return False - if timeout: - for key in self.times.keys(): - if self.times[key] < time.time() - 15: - self.times.pop(key) - if timeout in self.times: - return False - self.times[timeout] = time.time() - db = self.registryValue('database',channel) - if not db: - db,channel = fallback - if channel not in self.databases: - self.databases[channel] = sqlite.connect(os.path.join(datadir, '%s.db' % db)) - self.databases[channel].name = db - return self.databases[channel] - - def searchfactoid(self, irc, msg, match): - r"^!?search\s+(?P<query>.+)" - db = self._precheck(irc, msg, timeout=(msg.args[0],match.group('query'))) - if not db: return - cur = db.cursor() - query = '%%%s%%' % match.group('query').replace('%','').replace('*','%') - try: - cur.execute("SELECT name FROM facts WHERE (value LIKE %s OR name LIKE %s ) AND value NOT LIKE '<alias>%%'", (query, query)) - data = cur.fetchall() - all = [x[0] for x in data] - cur.execute("SELECT value FROM facts WHERE name LIKE %s AND value LIKE '<alias>%%'", query) - data = cur.fetchall() - all += [x[0][7:].strip() for x in data] - all = list(set(all)) - - if len(all) > 10: - irc.reply("Found: %s (and %d more)" % (', '.join(all[:10]), len(all)-10)) - elif len(all): - irc.reply("Found: %s" % ', '.join(all)) - else: - irc.reply("Found nothing") - except: - irc.error('An error occured (code 561)') - - def showfactoid(self, irc, msg, match): - r"^(!?ubotu\S?\s+|!)?(?P<noalias>-)?\s*(tell\s+(?P<nick>\S+)\s+about\s+)?(?P<factoid>\S.*?)(>\s*(?P<nick2>\S+).*)?$" - withnick = bool(match.group(1)) and msg.args[1].startswith('ubotu') - db = self._precheck(irc, msg, withnick=True, timeout=(msg.args[0], match.group('nick'), match.group('factoid'), match.group('nick2'))) - if not db: return - to = channel = msg.args[0] - if channel[0] != '#': - to = msg.nick - cur = db.cursor() - retmsg = '' - - noalias = match.group('noalias') - factoid = match.group('factoid').lower().strip() - if ' is ' in match.group(0) or \ - '=~' in match.group(0) or \ - '<sed>' in match.group(0) or \ - factoid.startswith('forget ') or \ - factoid.startswith('info ') or \ - factoid.startswith('find ') or \ - factoid.startswith('search ') or \ - factoid.startswith('seen'): - return - - #if channel.startswith('#'): - if True: - nick = match.group('nick') - if match.group('nick2'): nick = match.group('nick2') - if nick == 'me': nick = msg.nick - if nick: - if nick.lower() == 'ubotu': - irc.error("You lose.") - return - for chan in irc.state.channels: - if nick in irc.state.channels[chan].users and\ - msg.nick in irc.state.channels[chan].users: - retmsg = '%s wants you to know: ' % msg.nick - to = nick - break - else: - irc.error("That person could not be found in any channel you're in") - return - - # Retrieve factoid - try: - factoid = get_factoid(db, factoid, channel) - if not factoid: - irc.reply('I know nothing about %s - try searching http://bots.ubuntulinux.nl/factoids.cgi?db=%s' % (match.group('factoid'),db.name)) - return - # Output factoid - if noalias: - if not self._precheck(irc, msg, timeout=(to,factoid.name,1),withnick=True): - return - cur.execute("SELECT name FROM facts WHERE value = %s", '<alias> ' + factoid.name) - data = cur.fetchall() - if(len(data)): - #irc.queueMsg(ircmsgs.privmsg(to, "%s aliases: %s" % (factoid.name, ', '.join([x[0].strip() for x in data])))) - aliases = "%s aliases: %s" % (factoid.name, ', '.join([x[0].strip() for x in data])) - else: - if factoid.value.strip().startswith('<alias>'): - aliases = "%s is %s" % (factoid.name, factoid.value.strip()) - else: - aliases = "%s has no aliases" % factoid.name - authorinfo = "Added by %s on %s" % (factoid.author[:factoid.author.find('!')], factoid.added[:factoid.added.find('.')]) - irc.queueMsg(ircmsgs.privmsg(to,"%s - %s" % (aliases, authorinfo))) - else: - factoid = resolve_alias(db,factoid,channel) - # Do timing - if not self._precheck(irc, msg, timeout=(to,factoid.name,2),withnick=True): - return - cur.execute("UPDATE FACTS SET popularity = %d WHERE name = %s", factoid.popularity+1, factoid.name) - db.commit() - if factoid.value.startswith('<reply>'): - irc.queueMsg(ircmsgs.privmsg(to, '%s%s' % (retmsg, factoid.value[7:].strip()))) - else: - irc.queueMsg(ircmsgs.privmsg(to, '%s%s is %s' % (retmsg, factoid.name, factoid.value.strip()))) - # Now look for the -also factoid, but don't error on it - factoid = get_factoid(db, factoid.name + '-also', channel) - if not factoid: - return - if noalias: - if not self._precheck(irc, msg, timeout=(to,factoid.name,1)): - return - cur.execute("SELECT name FROM facts WHERE value = %s", '<alias> ' + factoid.name) - data = cur.fetchall() - if(len(data)): - aliases = "%s aliases: %s" % (factoid.name, ', '.join([x[0].strip() for x in data])) - else: - if factoid.value.strip().startswith('<alias>'): - aliases = "%s is %s" % (factoid.name, factoid.value.strip()) - else: - aliases = "%s has no aliases" % factoid.name - authorinfo = "Added by %s on %s" % (factoid.author[:factoid.author.find('!')], factoid.added[:factoid.added.find('.')]) - irc.queueMsg(ircmsgs.privmsg(to,"%s - %s" % (aliases, authorinfo))) - else: - factoid = resolve_alias(db,factoid,channel) - # Do timing - if not self._precheck(irc, msg, timeout=(to,factoid.name)): - return - cur.execute("UPDATE FACTS SET popularity = %d WHERE name = %s", factoid.popularity+1, factoid.name) - db.commit() - irc.queueMsg(ircmsgs.privmsg(to, '%s%s' % (retmsg, factoid.value.strip()))) - except: - raise - irc.error('An error occured (code 813)') - - def addfactoid(self, irc, msg, match): - r"^!?(?P<no>no,?\s+)?(?P<factoid>\S.*?)\s+is\s+(?P<also>also\s+)?(?P<fact>\S.*)" - factoid = match.group('factoid').lower().strip() - fact = match.group('fact').strip() - if '<sed>' in match.group(0) or \ - '=~' in match.group(0) or \ - factoid.startswith('forget') or \ - factoid.startswith('info') or \ - factoid.startswith('find') or \ - factoid.startswith('search'): - return - db = self._precheck(irc, msg, capability='editfactoids', timeout=(msg.args[0],match.group(0))) - if not db: return - channel = msg.args[0] - cur = db.cursor() - - if match.group('also'): - factoid = get_factoid(db, match.group('factoid'), channel) - if not factoid: - irc.reply('I know nothing about %s yet' % match.group('factoid')) - return - factoid = factoid.name + '-also' - - try: - # See if the alias exists and resolve it... - old_factoid = get_factoid(db, factoid, channel) - if old_factoid: - if not fact.startswith('<alias>'): - old_factoid = resolve_alias(db, old_factoid, channel) - # Unresolvable alias - if not old_factoid.name: - irc.reply(old_factoid.value) - return - if match.group('no'): - if fact.startswith('<alias>'): - cur.execute("SELECT COUNT(*) FROM facts WHERE value = %s", '<alias> ' + factoid) - num = cur.fetchall()[0][0] - if num: - irc.reply("Can't turn factoid with aliases into an alias") - return - alias_factoid = get_factoid(db, fact[7:].lower().strip(), channel) - if not alias_factoid: - alias_factoid = Factoid('','Error: unresolvable <alias>','','',0) - else: - alias_factoid = resolve_alias(db, alias_factoid, channel) - if not alias_factoid.name: - irc.reply(alias_factoid.value) - return - fact = '<alias> %s' % alias_factoid.name - fact = fact.lower() - cur.execute("""UPDATE facts SET value=%s, author=%s, added=%s WHERE name=%s""", - (fact, msg.prefix, str(datetime.datetime.now()), old_factoid.name)) - db.commit() - irc.reply("I'll remember that") - else: - irc.reply('%s is already known...' % factoid) - else: - if fact.lower().startswith('<alias>'): - old_factoid = get_factoid(db, fact[7:].lower().strip(), channel) - if not old_factoid: - old_factoid = Factoid('','Error: unresolvable <alias>','','',0) - else: - old_factoid = resolve_alias(db, old_factoid, channel) - if not old_factoid.name: - irc.reply(old_factoid.value) - return - fact = '<alias> %s' % old_factoid.name - fact = fact.lower() - cur.execute("""INSERT INTO facts (name, value, author, added) VALUES - (%s, %s, %s, %s)""", (factoid, fact, msg.prefix, str(datetime.datetime.now()))) - db.commit() - irc.reply("I'll remember that") - except: - irc.error('An error occured (code 735)') - - def editfactoid(self, irc, msg, match): - r"^!?(?P<factoid>.*?)\s*(=~|(\s+is\s*)<sed>)\s*s?(?P<regex>.*)" - db = self._precheck(irc, msg, capability='editfactoids', timeout=(msg.args[0],match.group(0))) - if not db: return - channel = msg.args[0] - cur = db.cursor() - - factoid = match.group('factoid').lower().strip() - regex = match.group('regex').strip() - if factoid.startswith('forget') or \ - factoid.startswith('info') or \ - factoid.startswith('find') or \ - factoid.startswith('search'): return - # Store factoid if nonexistant or 'no' is given - try: - # See if the alias exists and resolve it... - factoid = get_factoid(db, factoid, channel) - if factoid: - factoid = resolve_alias(db, factoid, channel) - # Unresolvable alias - if not factoid.name: - irc.reply(old_factoid.value) - return - delim = regex[0] - if regex[-1] != delim: - irc.reply("Missing end delimiter") - return - data = regex.split(delim)[1:-1] - if len(data) != 2: - irc.reply("You used the delimiter too often. Maybe try another one?") - return - regex, change = data - if '<alias>' in change.lower(): - irc.reply("Can't turn factoids into aliases this way") - return - try: - regex = re.compile(regex) - except: - irc.reply("Malformed regex") - return - newval = regex.sub(change, factoid.value, 1) - if newval != factoid.value: - cur.execute("""UPDATE facts SET value=%s, author=%s, added=%s WHERE name=%s""", - (newval, msg.prefix, str(datetime.datetime.now()), factoid.name)) - db.commit() - irc.reply("I'll remember that") - else: - irc.reply("No changes, not saving") - else: - irc.reply('I know nothing about %s' % match.group('factoid')) - except: - irc.error('An error occured (code 735)') - - def deletefactoid(self, irc, msg, match): - r"^!?forget\s+(?P<factoid>\S.*)" - db = self._precheck(irc, msg, capability='editfactoids', timeout=(msg.args[0],match.group('factoid'))) - if not db: return - channel = msg.args[0] - cur = db.cursor() - try: - cur.execute("SELECT COUNT(*) FROM facts WHERE value = %s", '<alias> ' + match.group('factoid')) - num = cur.fetchall()[0][0] - if num: - irc.reply("Can't forget factoids with aliases") - else: - cur.execute("DELETE FROM facts WHERE name = %s", match.group('factoid')) - cur.execute("DELETE FROM facts WHERE name = %s", match.group('factoid') + '-also') - db.commit() - irc.reply("I've forgotten it") - except: - raise - irc.error('An error occured (code 124)') - - aptcommand = """apt-cache\\ - -o"Dir::State::Lists=/home/dennis/ubugtu/data/apt/%s"\\ - -o"Dir::etc::sourcelist=/home/dennis/ubugtu/data/apt/%s.list"\\ - -o"Dir::State::status=/home/dennis/ubugtu/data/apt/%s.status"\\ - -o"Dir::Cache=/home/dennis/ubugtu/data/apt/cache"\\ - %s %s""" - def info(self, irc, msg, match): - r"^!?info\s+(?P<package>\S+)(\s+(?P<distro>\S+))?" - if not self._precheck(irc, msg, timeout=(msg.args[0],match.group('package'), match.group('distro'))): - return - distro = 'dapper' - if (match.group('distro') in ('warty','hoary','breezy','dapper','edgy','breezy-seveas','dapper-seveas','dapper-buntudot')): - distro = match.group('distro') - data = commands.getoutput(self.aptcommand % (distro, distro, distro, 'show', match.group('package'))) - data2 = commands.getoutput(self.aptcommand % (distro, distro, distro, 'showsrc', match.group('package'))) - if not data or 'E: No packages found' in data: - irc.reply('Package %s does not exist in %s' % (match.group('package'), distro)) - else: - maxp = {'Version': '0'} - packages = [x.strip() for x in data.split('\n\n')] - for p in packages: - if not p.strip(): - continue - parser = FeedParser.FeedParser() - parser.feed(p) - p = parser.close() - if apt_pkg.VersionCompare(maxp['Version'], p['Version']) < 0: - maxp = p - del parser - maxp2 = {'Version': '0'} - packages2 = [x.strip() for x in data2.split('\n\n')] - for p in packages2: - if not p.strip(): - continue - parser = FeedParser.FeedParser() - parser.feed(p) - p = parser.close() - if apt_pkg.VersionCompare(maxp2['Version'], p['Version']) < 0: - maxp2 = p - del parser - archs = '' - if maxp2['Architecture'] not in ('all','any'): - archs = ' (Only available for %s)' % maxp2['Architecture'] - irc.reply("%s: %s. In repository %s, is %s. Version %s (%s), package size %s kB, installed size %s kB%s" % - (maxp['Package'], maxp['Description'].split('\n')[0], r(distro, maxp['Section']), - maxp['Priority'], maxp['Version'], distro, int(maxp['Size'])/1024, maxp['Installed-Size'], archs)) - - def find(self, irc, msg, match): - r"^!?find\s+(?P<package>\S+)(\s+(?P<distro>\S+))?" - if not self._precheck(irc, msg, timeout=(msg.args[0],match.group('package'), match.group('distro'),2)): - return - distro = 'dapper' - if (match.group('distro') in ('warty','hoary','breezy','dapper','edgy')): - distro = match.group('distro') - data = commands.getoutput(self.aptcommand % (distro, distro, distro, 'search -n', match.group('package'))) - if not data: - irc.reply("No packages matching '%s' could be found" % match.group('package')) - else: - pkgs = [x.split()[0] for x in data.split('\n')] - if len(pkgs) > 5: - irc.reply("Found: %s (and %d others)" % (', '.join(pkgs[:5]), len(pkgs) -5)) - else: - irc.reply("Found: %s" % ', '.join(pkgs[:5])) - - def seen(self, irc, msg, match): - r"^!?seen\s+(?P<nick>\S+)" - if not self._precheck(irc, msg, timeout=(msg.args[0],match.group('nick'))): - return - to = msg.args[0] - if msg.args[0][0] != '#': - to = msg.nick - self.seens[match.group('nick')] = (to, time.time()) - irc.queueMsg(ircmsgs.privmsg('seenserv', "seen %s" % match.group('nick'))) - - def doNotice(self, irc, msg): - if msg.nick.lower() == 'seenserv': - resp = msg.args[1] - for n in self.seens.keys(): - if self.seens[n][1] < time.time() - 10: - self.seens.pop(n) - for n in self.seens.keys(): - if n.lower() in resp.lower(): - irc.queueMsg(ircmsgs.privmsg(self.seens[n][0], resp)) - self.seens.pop(n) - - def addeditor(self, irc, msg, args, name): - self._precheck(irc, msg, capability='addeditors') - try: - u = ircdb.users.getUser(name) - except: - irc.error('User %s is not registered' % name) - else: - u.addCapability('editfactoids') - irc.replySuccess() - addeditor = wrap(addeditor, ['text']) - - def editors(self, irc, msg, args): - irc.reply(', '.join([ircdb.users.getUser(u).name for u in ircdb.users.users \ - if 'editfactoids' in ircdb.users.getUser(u).capabilities])) - editors = wrap(editors) - def moderators(self, irc, msg, args): - irc.reply(', '.join([ircdb.users.getUser(u).name for u in ircdb.users.users \ - if 'addeditors' in ircdb.users.getUser(u).capabilities])) - moderators = wrap(moderators) - - def removeeditor(self, irc, msg, args, name): - self._precheck(irc, msg, capability='addeditors') - try: - u = ircdb.users.getUser(name) - except: - irc.error('User %s is not registered' % name) - else: - try: - u.removeCapability('editfactoids') - except: - irc.error('User %s is not an editor' % name) - else: - irc.replySuccess() - removeeditor = wrap(removeeditor, ['text']) - -Class = Encyclopedia -# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: