diff --git a/Bantracker/README.txt b/Bantracker/README.txt index f0b21aa..689bc28 100644 --- a/Bantracker/README.txt +++ b/Bantracker/README.txt @@ -9,35 +9,35 @@ kick/ban someone. It also uses commoncgi.py which should be on your sys.path (or as you can see in the script, sys.path is modified to include the dir of commoncgi.py) -The schema of the sqlite database: +The schema of the SQLite2 database: CREATE TABLE bans ( - id INTEGER PRIMARY KEY, - channel VARCHAR(30) NOT NULL, - mask VARCHAR(100) NOT NULL, - operator VARCHAR(30) NOT NULL, - time VARCHAR(300) NOT NULL, - removal DATETIME, - removal_op VARCHAR(30), - log TEXT - ); -CREATE TABLE comments ( - ban_id INTEGER, - who VARCHAR(100) NOT NULL, - comment MEDIUMTEXT NOT NULL, - time VARCHAR(300) NOT NULL - ); -CREATE TABLE sessions ( - session_id VARCHAR(50) PRIMARY KEY, - user MEDIUMTEXT NOT NULL, - time INT NOT NULL + id INTEGER PRIMARY KEY, + channel VARCHAR(30) NOT NULL, + mask VARCHAR(100) NOT NULL, + operator VARCHAR(30) NOT NULL, + time VARCHAR(300) NOT NULL, + removal DATETIME, + removal_op VARCHAR(30), + log TEXT ); -CREATE TABLE users ( - username VARCHAR(50) PRIMARY KEY, - salt VARCHAR(8), - password VARCHAR(50) +CREATE TABLE comments ( + ban_id INTEGER, + who VARCHAR(100) NOT NULL, + comment MEDIUMTEXT NOT NULL, + time VARCHAR(300) NOT NULL +); +CREATE TABLE sessions ( + session_id VARCHAR(50) PRIMARY KEY, + user MEDIUMTEXT NOT NULL, + time INT NOT NULL ); -To configure the plugin, create the sqlite database with above structure and set -supybot.plugins.bantracker.database to its filename. Then enable it per channel -by setting the channel variable supybot.plugins.bantracker.enabled +To configure the plugin, create the SQLite2 database with above structure and +set supybot.plugins.bantracker.database to its filename. Then enable it, either +per-channel or globally, by setting the channel variable: +supybot.plugins.bantracker.enabled +You can create the database by using the "sqlite" command-line tool by passing +the file name and then copy/paste the above table schema. +If you choose to enable this plugin during the initial setup (with the command +supybot-wizard), then the database will be created automatically for you. diff --git a/Bantracker/bans.cgi b/Bantracker/bans.cgi index 5f433a3..445ef6a 100755 --- a/Bantracker/bans.cgi +++ b/Bantracker/bans.cgi @@ -31,7 +31,8 @@ user = None # Delete old sessions try: - cur.execute("""DELETE FROM sessions WHERE time < %d""", int(time.time()) - 2592000 * 3) + session_timeout = int(time.time()) - (2592000 * 3) + cur.execute('DELETE FROM sessions WHERE time < %d', (session_timeout,)) except: pass @@ -41,7 +42,7 @@ if form.has_key('sess'): if cookie.has_key('sess'): try: sess = cookie['sess'].value - cur.execute("""SELECT user FROM sessions WHERE session_id=%s""",sess) + cur.execute('SELECT user FROM sessions WHERE session_id=%s',(sess,)) user = cur.fetchall()[0][0] except: con.commit() @@ -54,7 +55,7 @@ if not user: # Log if form.has_key('log'): - cur.execute("""SELECT log FROM bans WHERE id=%s""", form['log'].value) + cur.execute('SELECT log FROM bans WHERE id=%s', (form['log'].value,)) log = cur.fetchall() con.commit() if form.has_key('mark'): @@ -72,10 +73,10 @@ if form.has_key('log'): # Main page # Process comments if form.has_key('comment') and form.has_key('comment_id') and user: - cur.execute("""SELECT ban_id FROM comments WHERE ban_id=%s and comment=%s""", (form['comment_id'].value, form['comment'].value)) + cur.execute('SELECT ban_id FROM comments WHERE ban_id=%s and comment=%s', (form['comment_id'].value, form['comment'].value)) comm = cur.fetchall() if not len(comm): - cur.execute("""INSERT INTO comments (ban_id, who, comment, time) VALUES (%s, %s, %s, %s)""", + cur.execute('INSERT INTO comments (ban_id, who, comment, time) VALUES (%s, %s, %s, %s)', (form['comment_id'].value,user,form['comment'].value,pickle.dumps(datetime.datetime.now(pytz.UTC)))) con.commit() @@ -178,10 +179,10 @@ print 'Log' # Select and filter bans def getBans(id=None): - if id == None: - cur.execute("SELECT channel,mask,operator,time,removal,removal_op,id FROM bans ORDER BY id DESC") + if id is None: + cur.execute('SELECT channel,mask,operator,time,removal,removal_op,id FROM bans ORDER BY id DESC') else: - cur.execute("SELECT channel,mask,operator,time,removal,removal_op,id FROM bans ORDER BY id DESC WHERE id = %d", id) + cur.execute('SELECT channel,mask,operator,time,removal,removal_op,id FROM bans ORDER BY id DESC WHERE id = %d', (id,)) return cur.fetchall() def myfilter(item, regex, kick, ban, oldban, mute, oldmute, floods, operator, channel): @@ -302,7 +303,7 @@ for b in bans[start:end]: print ' class="bg2"' print '>' print '' - cur.execute("""SELECT who, comment, time FROM comments WHERE ban_id = %s""" % b[6]) + cur.execute('SELECT who, comment, time FROM comments WHERE ban_id = %d', (b[6],)) comments = cur.fetchall() if len(comments) == 0: print '(No comments) ' diff --git a/Bantracker/config.py b/Bantracker/config.py index 32e78eb..e25eb45 100644 --- a/Bantracker/config.py +++ b/Bantracker/config.py @@ -1,7 +1,7 @@ # -*- Encoding: utf-8 -*- ### # Copyright (c) 2005-2007 Dennis Kaarsemaker -# Copyright (c) 2008-2010 Terence Simpson +# Copyright (c) 2008-2011 Terence Simpson # Copyright (c) 2010 Elián Hanisch # # This program is free software; you can redistribute it and/or modify @@ -55,9 +55,10 @@ def configure(advanced): else: return review + output("If you choose not to enabled Bantracker for all channels, it can be enabled per-channel with the '@Config channel' command") enabled = yn("Enable Bantracker for all channels?") - database = something("Location of the Bantracker database", default=conf.supybot.directories.data.dirize('bans.db')) - bansite = anything("URL of the Bantracker web interface, without the 'bans.cgi'. (leave this blank if you don't want to run a web server)") + database = something("Location of the Bantracker database", default=Bantracker.database._default) + bansite = anything("URL of the Bantracker web interface, without the 'bans.cgi'. (leave this blank if you aren't running a web server)") request = yn("Enable review and comment requests from bot?", default=False) if request and advanced: @@ -67,29 +68,31 @@ def configure(advanced): type = set([]) for name in re.split(r',?\s+', types): name = name.lower() - if name in ('removal', 'ban', 'quiet'): + if name in ValidTypes.validStrings: type.add(name) output("Which nicks should be bot not requets comments from?") - output("Is case insensitive and wildcards '*' and '?' are accepted.") - ignores = anything("Separate types by spaces or commas:", default=', '.join(Bantracker.request.ignore._default)) + output("This is useful if you have automated channel bots that should not be directly asked for reviews") + output("Is case-insensitive and the wildcards '*' and '?' are accepted.") + ignores = anything("Separate nicks by spaces or commas:", default=', '.join(Bantracker.request.ignore._default)) ignore = set([]) for name in re.split(r',?\s+', ignores): name = name.lower() ignore.add(name) output("You can set the comment and review requests for some nicks to be forwarded to specific nicks/channels") + output("This is useful if you have automated channel bots that should not be directly asked for reviews") output("Which nicks should these requests be forwarded for?") - output("Is case insensitive and wildcards '*' and '?' are accepted.") - forwards = anything("Separate types by spaces or commas:", default=', '.join(Bantracker.request.forward._default)) + output("Is case-insensitive and the wildcards '*' and '?' are accepted.") + forwards = anything("Separate nicks by spaces or commas:", default=', '.join(Bantracker.request.forward._default)) forward = set([]) for name in re.split(r',?\s+', forwards): name = name.lower() forward.add(name) - output("Which nicks/channels should the requests be forwarded to?") - output("Is case insensitive and wildcards '*' and '?' are accepted.") - channels_i = anything("Separate types by spaces or commas:", default=', '.join(Bantracker.request.forward._default)) + output("Which nicks/channels should those requests be forwarded to?") + output("Is case-insensitive and wildcards '*' and '?' are accepted.") + channels_i = anything("Separate nicks/channels by spaces or commas:", default=', '.join(Bantracker.request.forward._default)) channels = set([]) for name in re.split(r',?\s+', channels_i): name = name.lower() @@ -122,6 +125,7 @@ def configure(advanced): Bantracker.database.setValue(db_file) if os.path.exists(db_file): + output("%r already exists" % db_file) return output("Creating an initial database in %r" % db_file) @@ -154,13 +158,6 @@ def configure(advanced): user MEDIUMTEXT NOT NULL, time INT NOT NULL )""") -#""" - - cur.execute("""CREATE TABLE users ( - username VARCHAR(50) PRIMARY KEY, - salt VARCHAR(8), - password VARCHAR(50) -)""") #""" except: @@ -176,7 +173,7 @@ Bantracker = conf.registerPlugin('Bantracker') conf.registerChannelValue(Bantracker, 'enabled', registry.Boolean(False, """Enable the bantracker""")) conf.registerGlobalValue(Bantracker, 'database', - registry.String('', "Filename of the bans database", private=True)) + registry.String(conf.supybot.directories.data.dirize('bans.db'), "Filename of the bans database", private=True)) conf.registerGlobalValue(Bantracker, 'bansite', registry.String('', "Web site for the bantracker, without the 'bans.cgi' appended", private=True)) diff --git a/Bugtracker/README.txt b/Bugtracker/README.txt index 8d49784..95eb5b8 100644 --- a/Bugtracker/README.txt +++ b/Bugtracker/README.txt @@ -1,17 +1,8 @@ -Copyright (c) 2005-2007, Dennis Kaarsemaker - -This program is free software; you can redistribute it and/or modify -it under the terms of version 2 of the GNU General Public License as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +Bug information reporting plugin, works with many well-known bugtrackers. The syntax to add a tracker is weird, here are some examples: @bugtracker add freedesktop bugzilla https://bugs.freedesktop.org Freedesktop -@bugtracker add malone malone https://launchpad.net/malone Malone +@bugtracker add launchpad launchpad https://launchpad.net/malone Launchpad @bugtracker add debian debbugs http://bugs.debian.org Debian @bugtracker add openoffice issuezilla http://www.openoffice.org/issues OpenOffice @bugtracker add django trac http://code.djangoproject.com/ticket Django @@ -21,21 +12,23 @@ In general: @bugtracker add [description] Bugtracker dialects (types) this plugin understands: * Bugzilla * Issuezilla (OpenOffice.org's tjernobyl transformation of bugzilla) -* Malone +* Launchpad (Including Ubuntu) * Debbugs (debbugs sucks donkeyballs - please fix debbugs) -* Trac (with not-too-buggered-up templates, it needs to do screenscraping) -* Sourceforge (needs atid and group_id in the url!) +* Trac * WikiForms (see bugs.gnewsense.org for an example) * str.php from the CUPS project * Mantis (http://www.mantisbt.org) +A notable exception is Sourceforge. Unfortunatly, it has no API or data export +feature to output bug information in a well-formed way. + To request a bug report, use this syntax: bug 123 bug #123 -supybot bug 123 +launchpad bug 123 bug 123, 4, 5 -bug 1, 3 and 89 +bugs 1, 3 and 89 To rename a bugtracker: @bugtracker rename old-name new-name @@ -45,47 +38,3 @@ existing tracker. The bug snarfing (responding to bug numbers/urls) will only work in channels where supybot.plugins.bugtracker.bugsnarfer is True. - -Automatic reporting of new bugs is also possible for Malone (the launchpad -bugtracker). Enabling this is not a trivial process. First step is to set the -supybot.plugins.bugtracker.reportercache variable to a dir for this purpose. You -also need a mail account that supports the + hack (mail for foo+bar@baz.com is -automatically delivered to foo@baz.com while the Delivered-To: header is set to -foo+bar@baz.com) which is accessible via IMAP. I know this is a rather strong -requirement, but that's the way it works now. Patches to make it work in other -situations are appreciated. - -Anyway, once that is all set up you're almost there. Let's assume the -mailaddress is bugreporter@yourdomain.com. Now pick a tag for your bugreports, -e.g. ubuntu (you can set a different tag per channel) and create a launchpad -account with address bugreporter+ubuntu@yourdomain.com. Activate that account -and make sure it gets bugmail for the product(s) you want to monitor. - -Now set the supybot.plugins.bugtracker.bugreporter in the channels where bugs -are to be reported to the value of the tag for bugs to be reported there and -watch bugs flowing in. - -To prevent old bugs from showing up when they change or a comment is being -added, you can manually fill the cache. Just touch files in the reporters cache -with the following name: - -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/config.py b/Bugtracker/config.py index 57f5c22..ac847f4 100644 --- a/Bugtracker/config.py +++ b/Bugtracker/config.py @@ -1,7 +1,7 @@ # -*- Encoding: utf-8 -*- ### # Copyright (c) 2005-2007 Dennis Kaarsemaker -# Copyright (c) 2008-2010 Terence Simpson +# Copyright (c) 2008-2011 Terence Simpson # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as @@ -45,13 +45,14 @@ def configure(advanced): else: return repeatdelay + output("Each of the next 3 questions can be set per-channel with the '@Config channel' command") bugSnarfer = yn("Enable detecting bugs numbers and URL in all channels?", default=Bugtracker.bugSnarfer._default) cveSnarfer = yn("Enable detecting CVE numbers and URL in all channels?", default=Bugtracker.cveSnarfer._default) oopsSnarfer = yn("Enable detecting Launchpad OOPS IDs in all channels?", default=Bugtracker.oopsSnarfer._default) if advanced: replyNoBugtracker = something("What should the bot reply with when a a user requests information from an unknown bug tracker?", default=Bugtracker.replyNoBugtracker._default) snarfTarget = something("What should be the default bug tracker used when one isn't specified?", default=Bugtracker.snarfTarget._default) - replyWhenNotFound = yn("Respond when a bug is not found?", default=Bugtracker.replyWhenNotFound._default) + replyWhenNotFound = yn("Should the bot report when a bug is not found?", default=Bugtracker.replyWhenNotFound._default) repeatdelay = getRepeatdelay() else: replyNoBugtracker = Bugtracker.replyNoBugtracker._default @@ -89,8 +90,8 @@ conf.registerChannelValue(Bugtracker, 'oopsSnarfer', enabled, such that any OOPS ### seen in the channel will have their information reported into the channel.""")) -conf.registerChannelValue(Bugtracker, 'bugReporter', - registry.String('', """Report new bugs (experimental)""")) +#conf.registerChannelValue(Bugtracker, 'bugReporter', +# registry.String('', """Report new bugs (experimental)""")) conf.registerChannelValue(Bugtracker, 'replyNoBugtracker', registry.String('I don\'t have a bugtracker %s.', """Determines the phrase @@ -98,7 +99,7 @@ conf.registerChannelValue(Bugtracker, 'replyNoBugtracker', bugtracker site.""")) conf.registerChannelValue(Bugtracker, 'snarfTarget', - registry.String('lp', """Determines the bugtracker to query when the + registry.String('launchpad', """Determines the bugtracker to query when the snarf command is triggered""")) conf.registerGlobalValue(Bugtracker, 'bugtrackers', @@ -117,18 +118,18 @@ conf.registerChannelValue(Bugtracker, 'showassignee', conf.registerChannelValue(Bugtracker, 'extended', registry.Boolean(False, "Show optional extneded bug information, specific to trackers")) -conf.registerGlobalValue(Bugtracker, 'reportercache', - registry.String('', """Name of the basedir for the bugreporter cache""", private=True)) +#conf.registerGlobalValue(Bugtracker, 'reportercache', +# registry.String('', """Name of the basedir for the bugreporter cache""", private=True)) -conf.registerGlobalValue(Bugtracker, 'imap_server', - registry.String('', """IMAP server for bugmail account""",private=True)) +#conf.registerGlobalValue(Bugtracker, 'imap_server', +# registry.String('', """IMAP server for bugmail account""",private=True)) -conf.registerGlobalValue(Bugtracker, 'imap_user', - registry.String('', """IMAP user for bugmail account""", private=True)) +#conf.registerGlobalValue(Bugtracker, 'imap_user', +# registry.String('', """IMAP user for bugmail account""", private=True)) -conf.registerGlobalValue(Bugtracker, 'imap_password', - registry.String('', """IMAP password for bugmail account""", private=True)) +#conf.registerGlobalValue(Bugtracker, 'imap_password', +# registry.String('', """IMAP password for bugmail account""", private=True)) -conf.registerGlobalValue(Bugtracker, 'imap_ssl', - registry.Boolean(False, """Use SSL for imap connections""", private=True)) +#conf.registerGlobalValue(Bugtracker, 'imap_ssl', +# registry.Boolean(False, """Use SSL for imap connections""", private=True)) diff --git a/Bugtracker/plugin.py b/Bugtracker/plugin.py index 1de58c4..0421c3d 100644 --- a/Bugtracker/plugin.py +++ b/Bugtracker/plugin.py @@ -24,13 +24,15 @@ import supybot.registry as registry import supybot.schedule as schedule import supybot.log as supylog -import re, os, time, imaplib, commands +#import imaplib +import re, os, time, commands import xml.dom.minidom as minidom from htmlentitydefs import entitydefs as entities import email.FeedParser import SOAPpy -bad_words = ["fuck","fuk","fucking","fuking","fukin","fuckin","fucked","fuked","fucker","shit","cunt","bastard","nazi","nigger","nigga","cock","bitches","bitch"] +# All the words below will be censored when reporting bug information +bad_words = set(["fuck","fuk","fucking","fuking","fukin","fuckin","fucked","fuked","fucker","shit","cunt","bastard","nazi","nigger","nigga","cock","bitches","bitch"]) def makeClean(s): words = s.split() @@ -95,7 +97,7 @@ class Bugtracker(callbacks.PluginRegexp): def __init__(self, irc): callbacks.PluginRegexp.__init__(self, irc) self.db = ircutils.IrcDict() - events = [] +# self.events = [] for name in self.registryValue('bugtrackers'): registerBugtracker(name) group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False) @@ -104,27 +106,28 @@ class Bugtracker(callbacks.PluginRegexp): else: self.log.warning("Bugtracker: Unknown trackertype: %s (%s)" % (group.trackertype(), name)) self.shorthand = utils.abbrev(self.db.keys()) - - # Schedule bug reporting self.shown = {} - #TODO: Remove everything below this line - if self.registryValue('imap_server') and self.registryValue('reportercache'): - try: - schedule.removeEvent(self.name() + '.bugreporter') - except: - pass - schedule.addPeriodicEvent(lambda: self.reportnewbugs(irc), 60, name=self.name() + '.bugreporter') - self.events += [self.name() + '.bugreporter'] - self.log.info('Bugtracker: Adding scheduled event "%s.bugreporter"' % self.name()) + +# # Schedule bug reporting +# #TODO: Remove everything below this line +# if self.registryValue('imap_server') and self.registryValue('reportercache'): +# try: +# schedule.removeEvent(self.name() + '.bugreporter') +# except: +# pass +# schedule.addPeriodicEvent(lambda: self.reportnewbugs(irc), 60, name=self.name() + '.bugreporter') +# self.events += [self.name() + '.bugreporter'] +# self.log.info('Bugtracker: Adding scheduled event "%s.bugreporter"' % self.name()) def die(self): #TODO: Remove me - try: - for event in self.events: - self.log.info('Bugtracker: Removing scheduled event "%s"' % event) - schedule.removeEvent(event) - schedule.removeEvent(self.name()) - except: - pass + pass +# try: +# for event in self.events: +# self.log.info('Bugtracker: Removing scheduled event "%s"' % event) +# schedule.removeEvent(event) +# schedule.removeEvent(self.name()) +# except: +# pass def is_ok(self, channel, tracker, bug): '''Flood/repeat protection''' @@ -138,95 +141,97 @@ class Bugtracker(callbacks.PluginRegexp): return False def is_new(self, tracker, tag, id): #Depricated - bugreporter_base = self.registryValue('reportercache') - if not os.path.exists(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id))): - try: - os.makedirs(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)))) - except: - pass - fd = open(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id)),'w') - fd.close() - return True - return False + pass +# bugreporter_base = self.registryValue('reportercache') +# if not os.path.exists(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id))): +# try: +# os.makedirs(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)))) +# except: +# pass +# fd = open(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id)),'w') +# fd.close() +# return True +# return False def reportnewbugs(self,irc): #Depricated - # Compile list of bugs - self.log.info("Bugtracker: Checking for new bugs") - bugs = {} - if self.registryValue('imap_ssl'): - sc = imaplib.IMAP4_SSL(self.registryValue('imap_server')) - else: - sc = imaplib.IMAP4(self.registryValue('imap_server')) - sc.login(self.registryValue('imap_user'), self.registryValue('imap_password')) - sc.select('INBOX') - new_mail = sc.search(None, '(UNSEEN)')[1][0].split()[:20] - - # Read all new mail - for m in new_mail: - msg = sc.fetch(m, 'RFC822')[1][0][1] - fp = email.FeedParser.FeedParser() - sc.store(m, '+FLAGS', "(\Deleted)") # Mark message deleted so we don't have to process it again - fp.feed(msg) - bug = fp.close() - tag = None - - if 'X-Launchpad-Bug' not in bug.keys(): - self.log.info('Bugtracker: Ignoring e-mail with no detectable bug (Not from Launchpad)') - continue - else: - tag = bug['X-Launchpad-Bug'] - if 'distribution=' not in tag and 'product=' not in tag: - self.log.info('Bugtracker: Ignoring e-mail with no detectable bug (no distro/product)') - continue - else: - tag = tag.split(';')[0].strip().replace("product=",'').replace("distribution=","") - - if not tag: - self.log.info('Bugtracker: Ignoring e-mail with no detectible bug (bad tag)') - - tag = tag[tag.find('+')+1:tag.find('@')] - if tag not in bugs: - bugs[tag] = {} - - # Determine bugtracker type (currently only Launchpad is supported anyway) - if bug['X-Launchpad-Bug']: - tracker = self.db['launchpad'] - id = int(bug['Reply-To'].split()[1]) - subj = bug['Subject']; - if '[NEW]' not in subj: #Not a new bug - continue - if self.is_new(tracker, tag, id): - component = bug['X-Launchpad-Bug'] - if 'component' in component: - component = component[component.find('component=')+10:] - component = component[:component.find(';')].replace('None','') - else: - component = '' - try: - if component: - bugs[tag][id] = self.get_bug('',tracker, id, False)[0].replace('"','(%s) "' % component, 1) - else: - bugs[tag][id] = self.get_bug('',tracker, id, False)[0] - if '[apport]' in bugs[tag][id]: - bugs[tag].pop(id) - except: - self.log.info("Bugtracker: Unable to get new bug %d" % id) - pass - else: - self.log.info('Bugtracker: Ignoring e-mail with no detectable bug') - - reported_bugs = 0 - - for c in irc.state.channels: - tags = self.registryValue('bugReporter', channel=c) - if not tags: - continue - for tag in tags.split(','): - if not tag or tag not in bugs.keys(): - continue - for b in sorted(bugs[tag].keys()): - irc.queueMsg(ircmsgs.privmsg(c,'New bug: #%s' % bugs[tag][b][bugs[tag][b].find('bug ')+4:])) - reported_bugs = reported_bugs+1 + pass +# # Compile list of bugs +# self.log.info("Bugtracker: Checking for new bugs") +# bugs = {} +# if self.registryValue('imap_ssl'): +# sc = imaplib.IMAP4_SSL(self.registryValue('imap_server')) +# else: +# sc = imaplib.IMAP4(self.registryValue('imap_server')) +# sc.login(self.registryValue('imap_user'), self.registryValue('imap_password')) +# sc.select('INBOX') +# new_mail = sc.search(None, '(UNSEEN)')[1][0].split()[:20] +# +# # Read all new mail +# for m in new_mail: +# msg = sc.fetch(m, 'RFC822')[1][0][1] +# fp = email.FeedParser.FeedParser() +# sc.store(m, '+FLAGS', "(\Deleted)") # Mark message deleted so we don't have to process it again +# fp.feed(msg) +# bug = fp.close() +# tag = None +# +# if 'X-Launchpad-Bug' not in bug.keys(): +# self.log.info('Bugtracker: Ignoring e-mail with no detectable bug (Not from Launchpad)') +# continue +# else: +# tag = bug['X-Launchpad-Bug'] +# if 'distribution=' not in tag and 'product=' not in tag: +# self.log.info('Bugtracker: Ignoring e-mail with no detectable bug (no distro/product)') +# continue +# else: +# tag = tag.split(';')[0].strip().replace("product=",'').replace("distribution=","") +# +# if not tag: +# self.log.info('Bugtracker: Ignoring e-mail with no detectible bug (bad tag)') +# +# tag = tag[tag.find('+')+1:tag.find('@')] +# if tag not in bugs: +# bugs[tag] = {} +# +# # Determine bugtracker type (currently only Launchpad is supported anyway) +# if bug['X-Launchpad-Bug']: +# tracker = self.db['launchpad'] +# id = int(bug['Reply-To'].split()[1]) +# subj = bug['Subject']; +# if '[NEW]' not in subj: #Not a new bug +# continue +# if self.is_new(tracker, tag, id): +# component = bug['X-Launchpad-Bug'] +# if 'component' in component: +# component = component[component.find('component=')+10:] +# component = component[:component.find(';')].replace('None','') +# else: +# component = '' +# try: +# if component: +# bugs[tag][id] = self.get_bug('',tracker, id, False)[0].replace('"','(%s) "' % component, 1) +# else: +# bugs[tag][id] = self.get_bug('',tracker, id, False)[0] +# if '[apport]' in bugs[tag][id]: +# bugs[tag].pop(id) +# except: +# self.log.info("Bugtracker: Unable to get new bug %d" % id) +# pass +# else: +# self.log.info('Bugtracker: Ignoring e-mail with no detectable bug') +# +# reported_bugs = 0 +# +# for c in irc.state.channels: +# tags = self.registryValue('bugReporter', channel=c) +# if not tags: +# continue +# for tag in tags.split(','): +# if not tag or tag not in bugs.keys(): +# continue +# for b in sorted(bugs[tag].keys()): +# irc.queueMsg(ircmsgs.privmsg(c,'New bug: #%s' % bugs[tag][b][bugs[tag][b].find('bug ')+4:])) +# reported_bugs = reported_bugs+1 def add(self, irc, msg, args, name, trackertype, url, description): """ [] @@ -359,18 +364,18 @@ class Bugtracker(callbacks.PluginRegexp): msg.tag('nbugs', nbugs + len(bugids)) bt = map(lambda x: x.lower(), match.group('bt').split()) # Strip off trailing ':' from the tracker name. Allows for (LP: #nnnnnn) - if len(bt) and bt[0].endswith(':'): + if bt and bt[0].endswith(':'): bt[0] = bt[:-1] name = '' if len(bt) == 1 and not (bt[0] in ['bug','bugs']): try: - name = bt[0].lower() + name = bt[0] tracker = self.db[name] except: return elif len(bt) == 2: try: - name = bt[0].lower() + name = bt[0] tracker = self.db[name] except: name = '' diff --git a/COPYING b/COPYING index 8d8d32d..5fc6d1c 100644 --- a/COPYING +++ b/COPYING @@ -1,10 +1,10 @@ -Most of the code in this package is licensed under the GNU GPL Version 2, -the exception is the Lart plugin, which has a BSD-style license. -See Lart/__init__.py for the license. +Unless otherwise specified, the code in this package is licensed under the +GNU GPL Version 2 license, Refer to each of the files in plugin directories +for specifics. NOTE! The GPL below is copyrighted by the Free Software Foundation, but -the instance of code that it refers to (the kde programs) are copyrighted -by the authors who actually wrote it. +the instance of code that it refers to (the plugins) are copyrighted +by the authors who actually wrote them. --------------------------------------------------------------------------- diff --git a/Encyclopedia/README.txt b/Encyclopedia/README.txt index 862ff61..27fcfa6 100644 --- a/Encyclopedia/README.txt +++ b/Encyclopedia/README.txt @@ -1,23 +1,16 @@ -Copyright (c) 2006-2007, Dennis Kaarsemaker - -This program is free software; you can redistribute it and/or modify -it under the terms of version 2 of the GNU General Public License as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -This plugin used to have package lookup, this was mooved to the PackageInfo -plugin. +Factoid plugin +Note: This plugin used to have package lookup, this was mooved to the +PackageInfo plugin. Pick a name for your database. A lowercase-only name without spaces is probably best, this example wil use myfactoids as name. Then create a directory to store -your databases in (somewere in $botdir/data would be best). In the new directory -create an sqlite database with the following command: +your databases in (somewere in $botdir/data would be best). +If you choose to enable this plugin during supybot-wizard the database will be +created for you. If noy, you can create the database manually. +In the new directory create an SQLite2 database with the following command: sqlite myfactoids.db +Then copy/paste in the below 2 tables: CREATE TABLE facts ( id INTEGER PRIMARY KEY, @@ -36,6 +29,7 @@ CREATE TABLE log ( oldvalue VARCHAR(200) NOT NULL ); + If you want to create more databases, repeat these last two steps. When the databases exist, you need to configure the bots to actually use them. @@ -44,9 +38,10 @@ dirand the channel value supybot.plugins.encyclopedia.database to the name of the database (without the .db suffix). Documentation on adding/editing factoids can be found on -https://wiki.ubuntu.com/UbuntuBots To give people edit access, let them register -with your bot and use: %addeditor nickname_here (replace % with your prefix -char). Similarly you can use removeeditor :). +https://ubottu.com/devel/wiki/Plugins#Encyclopedia +To give people edit access, let them register with your bot and use the command: +@addeditor nickname_here +(replace @ with your prefix char). Similarly you can use removeeditor :). The web interface is a simple cgi script with some templates, css and the commoncgi.py file from the bzr tree. Make sure you set the variables datadir and diff --git a/Encyclopedia/config.py b/Encyclopedia/config.py index 2490a30..e4c488c 100644 --- a/Encyclopedia/config.py +++ b/Encyclopedia/config.py @@ -105,12 +105,12 @@ def configure(advanced): db_file = Encyclopedia.database() if not db_dir: - db_dir = conf.supybot.directories.data() + db_dir = Encyclopedia.datadir._default output("supybot.plugins.Encyclopedia.datadir will be set to %r" % db_dir) Encyclopedia.datadir.setValue(db_dir) if not db_file: - db_file = 'ubuntu' + db_file = Encyclopedia.database._default output("supybot.plugins.Encyclopedia.database will be set to %r" % db_file) Encyclopedia.database.setValue(db_dir) diff --git a/Encyclopedia/plugin.py b/Encyclopedia/plugin.py index e0c7d8a..e6171be 100644 --- a/Encyclopedia/plugin.py +++ b/Encyclopedia/plugin.py @@ -31,13 +31,14 @@ else: import sre as re def checkIgnored(hostmask, recipient='', users=ircdb.users, channels=ircdb.channels): - if ircdb.ignores.checkIgnored(hostmask): - return True try: id = ircdb.users.getUserId(hostmask) user = users.getUser(id) except KeyError: # If there's no user... + if ircdb.ignores.checkIgnored(hostmask): + return True + if ircutils.isChannel(recipient): channel = channels.getChannel(recipient) if channel.checkIgnored(hostmask): @@ -46,9 +47,13 @@ def checkIgnored(hostmask, recipient='', users=ircdb.users, channels=ircdb.chann return False else: return False + if user._checkCapability('owner'): # Owners shouldn't ever be ignored. return False + + if ircdb.ignores.checkIgnored(hostmask): + return True elif user.ignore: return True elif recipient: diff --git a/PackageInfo/README.txt b/PackageInfo/README.txt index f24b145..9ac01e2 100644 --- a/PackageInfo/README.txt +++ b/PackageInfo/README.txt @@ -1,22 +1,26 @@ This plugin allows package lookup via apt-cache/apt-file +If you choose to enable this plugin from the supybot-wizard command, then most +of the setup will be automatically done for you. You may still change the values +of the settings manaually. + --Setup-- supybot.plugins.PackageInfo.aptdir: Directory to use to store the apt cache (Global) -Default: '' +Default: '$BOTDIR/data/aptdir' Create a new empty directory that will be used for the apt cache. In this directory, you create sources.list files for every release you want to search. The name of the file is important, since the filename (without the .list suffix) is the name that is used to refer to the release. The .list file should contain _both_ the deb and deb-src source lines. -Eg: -deb http://archive.ubuntu.com/ubuntu jaunty main restricted universe multiverse -deb-src http://archive.ubuntu.com/ubuntu jaunty main restricted universe multiverse +Eg; in lucid.list: +deb http://archive.ubuntu.com/ubuntu ludid main restricted universe multiverse +deb-src http://archive.ubuntu.com/ubuntu lucid main restricted universe multiverse supybot.plugins.PackageInfo.defaultRelease: Set this to the default release to use when none is specified. (Channel) -Default: '' +Default: 'lucid' Whenever you create a new .list file, it is important to run the update_apt and update_apt_file scripts that comes with this plugin. Before you run these, diff --git a/PackageInfo/config.py b/PackageInfo/config.py index 7e92928..d91cd16 100644 --- a/PackageInfo/config.py +++ b/PackageInfo/config.py @@ -43,17 +43,17 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse if enabled and advanced: prefixchar = something("Which prefix character should be bot respond to?", default=PackageInfo.prefixchar._default) defaultRelease = something("What should be the default distrobution when not specified?", default=PackageInfo.defaultRelease._default) - aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=conf.supybot.directories.data.dirize('aptdir')) + aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=PackageInfo.aptdir._default) # People tend to thing this should be /var/cache/apt - while aptdir.startswith('/var'): + while aptdir.startswith('/var'): #NOTE: This is not a good hack. Maybe just blacklist /var/cache/apt (or use apt to report back the cache dir) output("NO! Do not use your systems apt directory") - aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=conf.supybot.directories.data.dirize('aptdir')) + aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=PackageInfo.aptdir._default) else: prefixchar = PackageInfo.prefixchar._default defaultRelease = PackageInfo.defaultRelease._default - aptdir = conf.supybot.directories.data.dirize('aptdir') + aptdir = PackageInfo.aptdir._default PackageInfo.enabled.setValue(enabled) @@ -61,7 +61,7 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse PackageInfo.prefixchar.setValue(prefixchar) PackageInfo.defaultRelease.setValue(defaultRelease) - default_dists = set(['hardy', 'jaunty', 'karmic', 'lucid', 'maveric']) + default_dists = set(['dapper', 'hardy', 'lucid', 'maveric', 'natty', 'oneiric']) pluginDir = os.path.abspath(os.path.dirname(__file__)) update_apt = os.path.join(pluginDir, 'update_apt') update_apt_file = os.path.join(pluginDir, 'update_apt_file') @@ -115,7 +115,7 @@ conf.registerChannelValue(PackageInfo, 'enabled', conf.registerChannelValue(PackageInfo, 'prefixchar', conf.ValidPrefixChars('!', "Character the bot will respond to")) conf.registerChannelValue(PackageInfo, 'defaultRelease', - registry.String('lucid', "Default release to use when none is specified")) + registry.String('natty', "Default release to use when none is specified")) conf.registerGlobalValue(PackageInfo, 'aptdir', conf.Directory(conf.supybot.directories.data.dirize('aptdir'), "Path to the apt directory", private=True)) diff --git a/PackageInfo/update_apt b/PackageInfo/update_apt old mode 100644 new mode 100755 index 4c7a035..6b1bb66 --- a/PackageInfo/update_apt +++ b/PackageInfo/update_apt @@ -1,11 +1,14 @@ #!/usr/bin/env bash +# Set DIR to the same value as supybot.plugins.PackageInfo.aptdir if [ -z "$DIR" ]; then DIR=/home/bot/aptdir fi +# Be quiet bt default DEFAULT_OPTS="-qq" +# Check command-line arguments while [ "x$1" != "x" ]; do case "$1" in -v|--verbose) @@ -27,7 +30,7 @@ while [ "x$1" != "x" ]; do exit 1 ;; *) - echo "This script takes no arguments" >&2 + echo "This script takes no non-argument parameters" >&2 exit 1 ;; esac diff --git a/PackageInfo/update_apt_file b/PackageInfo/update_apt_file old mode 100644 new mode 100755 index b92b950..d1a3fed --- a/PackageInfo/update_apt_file +++ b/PackageInfo/update_apt_file @@ -5,14 +5,17 @@ if [ ! -x "$(which apt-file)" ]; then exit 1 fi +# Set DIR to the same value as supybot.plugins.PackageInfo.aptdir if [ -z "$DIR" ]; then DIR=/home/bot/aptdir fi +# Be quiet by default if [ -z "$VERBOSE" ]; then VERBOSE="no" fi +# Check command-line arguments while [ "x$1" != "x" ]; do case "$1" in -v|--verbose) @@ -31,7 +34,7 @@ while [ "x$1" != "x" ]; do exit 1 ;; *) - echo "This script takes no arguments" >&2 + echo "This script takes no non-argument parameterss" >&2 exit 1 ;; esac diff --git a/README.txt b/README.txt index 2567644..0de51a8 100644 --- a/README.txt +++ b/README.txt @@ -6,8 +6,9 @@ setting up an ubottu clone for the first time. These plugins are designed to work with Python 2.5 and Python 2.6, they are untested and unsupported on Python 3.0. The recommended way to set-up these plugins is to first create a directory for the bot, then move this directory to -there and rename it to "plugins". After that you should make sure you have the -following installed on the system: +there and rename it to "plugins". Alternatively you can create an empty plugins +directory and create links to each separate plugin directory inside there. +After that you should make sure you have the following installed on the system: Name Debian/Ubuntu package Website Python-apt python-apt N/A Debian (and derivatives) only @@ -19,6 +20,8 @@ SOAPpy python-soappy http://soapy.sourceforge.net/ Launchpadlib python-launchpadlib https://launchpad.net/launchpadlib apt-file apt-file N/A Debian (and derivatives) only +Launchpadlib will become a required module for Bugtracker (and possibly others) + Once these packages are installed, and in the bot directory containing the "plugins" sub-directory, run this command: "supybot-wizard". This wizard will guide you through the process of setting up the bot for an IRC @@ -32,11 +35,11 @@ Bantracker Helps to track bans/kicks/quiets/removes in channels Bugtracker Show information on bugs for various bug trackers. Encyclopedia A factoid encyclopaedia. IRCLogin Allows password-less login from users identified to services. -Lart A database of "larts". -Mess Random mess, pulls random things from the internet. +Lart A database of "larts". (Unmaintained) +Mess Random mess, pulls random things from the internet. (Unmaintained) PackageInfo Lookup information on Debian packages and file search. -(works on Debian and derivatives only) -Webcal Updates a channel topic based on events in an iCal. +(works on Debian and derivatives only, unless you take special measures) +Webcal Updates a channel topic based on events in an iCal. (Unmaintained) Note: Mess and Lart are largely unmaintained but are working, Webcal is unmaintained and mostly broken except for extremely simple iCal feeds. @@ -44,12 +47,16 @@ unmaintained and mostly broken except for extremely simple iCal feeds. If you chose to enable Bantracker or Encyclopedia, initial databases will be created in the "data" directory. These are named "bans.db" for the Bantracker plugin and "ubuntu.db" for the Encyclopedia plugin. You can obtain the same -database that ubottu uses by overwriting the "bans/ubuntu.db" file with the one +database that ubottu uses by overwriting the "ubuntu.db" file with the one located at http://ubottu.com/ubuntu.db or by running the "@sync" command with -the bot in IRC. If you enabled the PackageInfo plugin several .list files will -be created in "data/aptdir/", these will be used with the "apt-cache" and -"apt-file" commands to retrieve package information and find files within -packages. When asked if you want to run the "update_apt" script you should +the bot in IRC. +The Bantracker database from ubottu is not available to the +public, as it will contain logs of channels which are not publically logged. + +If you enabled the PackageInfo plugin several .list files will be created in +"data/aptdir/", these will be used with the "apt-cache" and "apt-file" commands +to retrieve package information and find files within packages. +When asked if you want to run the "update_apt" script you should answer "y" to download the package information, this will take a while depending on the speed of your connection and proximity to the default servers. The same is true for the "update_apt_file" script, which will only be ran if @@ -62,5 +69,7 @@ the user the bot is run as. Once you have selected the plugins you want to enable, you will be asked "Would you like to set the prefix char(s) for your bot?", you should answer "y" and set it to anything other than the prefix character for Encyclopedia and -PacakgeInfo. If you weren't asked, it defaults to '!'. The recommended -character is '@'. +PacakgeInfo. If you weren't asked, it defaults to '!' for those plugins. The +recommended character to use is '@'. Do not set the prefix chacter for commands +and for the plugins to the same value, you will run into trouble. + diff --git a/commoncgi.py b/commoncgi.py index ca4cd57..e21ae3e 100644 --- a/commoncgi.py +++ b/commoncgi.py @@ -31,6 +31,7 @@ if cookie.has_key('tz'): cookie['tz']['version'] = 1 class IOWrapper: + '''Class to wrap default IO, used with templates''' def __init__(self): self.buf = [] def write(self, val): @@ -42,6 +43,7 @@ sys.stdout = IOWrapper() sys.stderr = IOWrapper() def send_page(template): + '''Sends a template page and exit''' data = sys.stdout.getvalue() errdata = sys.stderr.getvalue() sys.stdout = sys.__stdout__ @@ -64,5 +66,6 @@ def send_page(template): sys.exit(0) def q(txt): + '''Simple HTML entity quoting''' return txt.replace('&','&').replace('<','<').replace('>','>').replace('"','"')