Bugtracker now is much more usable and contains documentation. Backwards

incompatible though!
This commit is contained in:
Dennis Kaarsemaker
2007-02-01 09:46:32 +01:00
parent 4ec4305038
commit 139d186c83
3 changed files with 78 additions and 540 deletions

View File

@ -26,6 +26,7 @@ Bugtracker dialects (types) this plugin understands:
* Trac (with not-too-buggered-up templates, it needs to do screenscraping) * Trac (with not-too-buggered-up templates, it needs to do screenscraping)
* Sourceforge (needs atid and group_id in the url!) * Sourceforge (needs atid and group_id in the url!)
* WikiForms (see bugs.gnewsense.org for an example) * WikiForms (see bugs.gnewsense.org for an example)
* str.php from the CUPS project
To request a bug report, use this syntax: 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 If your products already have many bugreports, consider doing some
screenscraping with the malone searchpages and sed/awk :) 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

View File

@ -230,7 +230,7 @@ class Bugtracker(callbacks.PluginRegexp):
irc.error(s % name) irc.error(s % name)
remove = wrap(remove, ['text']) remove = wrap(remove, ['text'])
def rename(self, irc, msg, args, oldname, newname): def rename(self, irc, msg, args, oldname, newname, newdesc):
"""<oldname> <newname> """<oldname> <newname>
Rename the bugtracker associated with <oldname> to <newname> Rename the bugtracker associated with <oldname> to <newname>
@ -238,8 +238,11 @@ class Bugtracker(callbacks.PluginRegexp):
try: try:
name = self.shorthand[oldname.lower()] name = self.shorthand[oldname.lower()]
group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False) group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False)
self.db[newname] = defined_bugtrackers[trackertype](name,group.url(),group.description()) d = group.description()
registerBugtracker(newname, group.url(), group.description(), group.trackertype()) 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] del self.db[name]
self.registryValue('bugtrackers').remove(name) self.registryValue('bugtrackers').remove(name)
self.shorthand = utils.abbrev(self.db.keys()) self.shorthand = utils.abbrev(self.db.keys())
@ -247,7 +250,7 @@ class Bugtracker(callbacks.PluginRegexp):
except KeyError: except KeyError:
s = self.registryValue('replyNoBugtracker', msg.args[0]) s = self.registryValue('replyNoBugtracker', msg.args[0])
irc.error(s % name) irc.error(s % name)
rename = wrap(rename, ['something','something']) rename = wrap(rename, ['something','something', additional('text')])
def list(self, irc, msg, args, name): def list(self, irc, msg, args, name):
"""[abbreviation] """[abbreviation]
@ -275,7 +278,7 @@ class Bugtracker(callbacks.PluginRegexp):
list = wrap(list, [additional('text')]) list = wrap(list, [additional('text')])
def bugSnarfer(self, irc, msg, match): def bugSnarfer(self, irc, msg, match):
r"""\b(?P<bt>(([a-z]+)?\s+bugs?|[a-z]+))\s+#?(?P<bug>\d+(?!\d*\.\d+)((,|\s*(and|en|et|und|ir))\s*#?\d+(?!\d*\.\d+))*)""" r"""\b(?P<bt>(([a-z0-9]+)?\s+bugs?|[a-z]+))\s+#?(?P<bug>\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]): if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]):
return return
@ -327,6 +330,8 @@ class Bugtracker(callbacks.PluginRegexp):
continue continue
try: try:
report = self.get_bug(tracker,bugid,self.registryValue('showassignee', msg.args[0])) 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: except BugtrackerError, e:
if 'private' in str(e): if 'private' in str(e):
irc.reply("Bug %d on http://launchpad.net/bugs/%d is private" % (bugid, bugid)) 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) irc.reply(r, prefixNick=False)
def turlSnarfer(self, irc, msg, match): def turlSnarfer(self, irc, msg, match):
"(?P<tracker>https?://.*?)(show_bug.cgi\?id=|bugreport.cgi\?bug=|(bugs|\+bug)/|/ticket/|tracker/.*aid=)(?P<bug>\d+)(?P<sfurl>&group_id=\d+&at_id=\d+)?" r"(?P<tracker>https?://\S*?)(show_bug.cgi\?id=|bugreport.cgi\?bug=|(bugs|\+bug)/|/ticket/|tracker/\S*aid=)(?P<bug>\d+)(?P<sfurl>&group_id=\d+&at_id=\d+)?"
if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]): if msg.args[0][0] == '#' and not self.registryValue('bugSnarfer', msg.args[0]):
return return
try: try:
@ -447,6 +452,8 @@ class Bugzilla(IBugtracker):
bug_n = zilladom.getElementsByTagName('bug')[0] bug_n = zilladom.getElementsByTagName('bug')[0]
if bug_n.hasAttribute('error'): if bug_n.hasAttribute('error'):
errtxt = bug_n.getAttribute('error') errtxt = bug_n.getAttribute('error')
if errtxt == 'NotFound':
raise BugNotFoundError
s = 'Error getting %s bug #%s: %s' % (self.description, id, errtxt) s = 'Error getting %s bug #%s: %s' % (self.description, id, errtxt)
raise BugtrackerError, s raise BugtrackerError, s
try: try:
@ -458,7 +465,11 @@ class Bugzilla(IBugtracker):
pass pass
component = _getnodetxt(bug_n.getElementsByTagName('component')[0]) component = _getnodetxt(bug_n.getElementsByTagName('component')[0])
severity = _getnodetxt(bug_n.getElementsByTagName('bug_severity')[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: except Exception, e:
s = 'Could not parse XML returned by %s bugzilla: %s' % (self.description, e) s = 'Could not parse XML returned by %s bugzilla: %s' % (self.description, e)
raise BugtrackerError, s raise BugtrackerError, s
@ -475,6 +486,8 @@ class Issuezilla(IBugtracker):
raise BugtrackerError, s raise BugtrackerError, s
bug_n = zilladom.getElementsByTagName('issue')[0] bug_n = zilladom.getElementsByTagName('issue')[0]
if not (bug_n.getAttribute('status_code') == '200'): 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')) s = 'Error getting %s bug #%s: %s' % (self.description, id, bug_n.getAttribute('status_message'))
raise BugtrackerError, s raise BugtrackerError, s
try: try:
@ -634,21 +647,25 @@ class Trac(IBugtracker):
try: try:
bugdata = utils.web.getUrl(url) bugdata = utils.web.getUrl(url)
except Exception, e: 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) s = 'Could not parse data returned by %s: %s' % (self.description, e)
raise BugtrackerError, s raise BugtrackerError, s
for l in bugdata.split("\n"): for l in bugdata.split("\n"):
if '<h1>Ticket' in l:
severity = l[l.find('(')+1:l.find(')')]
if 'class="summary"' in l: if 'class="summary"' in l:
title = l[l.find('>')+1:l.find('</')] title = l[l.find('>')+1:l.find('</')]
if 'class="status"' in l: if 'class="status"' in l:
status = l[l.find('<strong>')+8:l.find('</strong>')] status = l[l.find('>(')+2:l.find(')')]
if 'headers="h_component"' in l: if 'headers="h_component"' in l:
package = l[l.find('>')+1:l.find('</')] package = l[l.find('>')+1:l.find('</')]
if 'headers="h_severity"' in l: if 'headers="h_severity"' in l:
severity = l[l.find('>')+1:l.find('</')] severity = l[l.find('>')+1:l.find('</')]
if 'headers="h_stage"' in l:
severity = l[l.find('>')+1:l.find('</')]
if 'headers="h_owner"' in l: if 'headers="h_owner"' in l:
assignee = l[l.find('>')+1:l.find('</')] assignee = l[l.find('>')+1:l.find('</')]
print [(id, package, title, severity, status, assignee, "%s/%s" % (self.url, id))]
return [(id, package, title, severity, status, assignee, "%s/%s" % (self.url, id))] return [(id, package, title, severity, status, assignee, "%s/%s" % (self.url, id))]
class WikiForms(IBugtracker): class WikiForms(IBugtracker):
@ -662,6 +679,8 @@ class WikiForms(IBugtracker):
try: try:
bugdata = utils.web.getUrl(url) bugdata = utils.web.getUrl(url)
except Exception, e: except Exception, e:
if 'HTTP Error 404' in str(e):
raise BugNotFoundError
s = 'Could not parse data returned by %s: %s' % (self.description, e) s = 'Could not parse data returned by %s: %s' % (self.description, e)
raise BugtrackerError, s raise BugtrackerError, s
for l in bugdata.split("\n"): for l in bugdata.split("\n"):
@ -676,6 +695,35 @@ class WikiForms(IBugtracker):
package = strip_tags(l[l.find('<dd>')+4:]) package = strip_tags(l[l.find('<dd>')+4:])
return [(id, package, title, severity, status, '', "%s/%05d" % (self.url, id))] 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:</th>' in l2:
severity = 'Priority ' + l[l.find(' - ')+3:min(l.find(','),l.find('</td>'))]
if '>application:</th>' in l2:
package = l[l.find('<td>')+4:l.find('</td>')]
if 'nowrap>status:</th>' in l2:
status = l[l.find(' - ')+3:l.find('</td>')]
if 'nowrap>summary:</th>' in l2:
title = l[l.find('<td>')+4:l.find('</td>')]
if 'nowrap>assigned to:</th>' in l2:
assignee = strip_tags(l[l.find('<td>')+4:l.find('</td>')])
if assignee == 'Unassigned':
assignee = 'nobody'
return [(id, package, title, severity, status, assignee, "%s/L%d" % (self.url, id))]
sfre = re.compile(r""" sfre = re.compile(r"""
.*? .*?
<h2>\[.*?\]\s*(?P<title>.*?)</h2> <h2>\[.*?\]\s*(?P<title>.*?)</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): if type(v[k]) == type(IBugtracker) and issubclass(v[k], IBugtracker) and not (v[k] == IBugtracker):
defined_bugtrackers[k.lower()] = v[k] defined_bugtrackers[k.lower()] = v[k]
# Let's add a few bugtrackers by default
registerBugtracker('mozilla', 'http://bugzilla.mozilla.org', 'Mozilla', 'bugzilla') registerBugtracker('mozilla', 'http://bugzilla.mozilla.org', 'Mozilla', 'bugzilla')
registerBugtracker('ubuntu', 'http://bugzilla.ubuntu.com', 'Ubuntu', 'bugzilla') registerBugtracker('ubuntu', 'http://bugzilla.ubuntu.com', 'Ubuntu', 'bugzilla')
registerBugtracker('gnome', 'http://bugzilla.gnome.org', 'Gnome', '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('ximian', 'http://bugzilla.ximian.com', 'Ximian', 'bugzilla')
registerBugtracker('freedesktop', 'http://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla') registerBugtracker('freedesktop', 'http://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla')
registerBugtracker('freedesktop2', 'http://bugs.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') 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') 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') 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('trac', 'http://projects.edgewall.com/trac/ticket', 'Trac', 'trac')
registerBugtracker('django', 'http://code.djangoproject.com/ticket', 'Django', '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') 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') registerBugtracker('sourceforge', 'http://sourceforge.net/tracker/', 'Sourceforge', 'sourceforge')
Class = Bugtracker Class = Bugtracker

View File

@ -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: