(large commit, see bazaar log)

[Bantracker]
 * Less spaces in README.txt
 * Remove mention of table 'users' in README.txt
 * Add more detail on how to create the bans database in README.txt
 * Add note about supybot-wizard creating the initial database in README.txt
 * Don't hard-code default values in config.py:configure()
 * Tweak config.py
 * Clean up bans.cgi a bit

[Bugtracker]
 * Comment-out obsolete "bug reporting" variables in config.py
 * Update README.txt and remove "bug reporting" stuff, also remove extraneous license info
 * Comment-out obsolete "bug reporting" code
 * Don't import imaplib

[Encyclopedia]
 * Don't hard-code default values in config.py:configure()
 * Check for 'owner' capability before checking if the hostmask is ignored in plugin.py:checkIgnored()
 * Clean up README.txt

[PackageInfo]
 * Don't hard-code default values in config.py:configure()
 * Update default distributions in config.py:configure()
 * Update defaultRelease in config.py 
 * Update README.txt

* Add a few docstrings to commoncgi.py
* Update COPYING
* Update README.txt
This commit is contained in:
Terence Simpson 2011-05-28 07:33:21 +01:00
parent 6fe55f7eb1
commit fd36bffcc0
16 changed files with 269 additions and 294 deletions

View File

@ -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 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 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 ( CREATE TABLE bans (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
channel VARCHAR(30) NOT NULL, channel VARCHAR(30) NOT NULL,
mask VARCHAR(100) NOT NULL, mask VARCHAR(100) NOT NULL,
operator VARCHAR(30) NOT NULL, operator VARCHAR(30) NOT NULL,
time VARCHAR(300) NOT NULL, time VARCHAR(300) NOT NULL,
removal DATETIME, removal DATETIME,
removal_op VARCHAR(30), removal_op VARCHAR(30),
log TEXT 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
); );
CREATE TABLE users ( CREATE TABLE comments (
username VARCHAR(50) PRIMARY KEY, ban_id INTEGER,
salt VARCHAR(8), who VARCHAR(100) NOT NULL,
password VARCHAR(50) 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 To configure the plugin, create the SQLite2 database with above structure and
supybot.plugins.bantracker.database to its filename. Then enable it per channel set supybot.plugins.bantracker.database to its filename. Then enable it, either
by setting the channel variable supybot.plugins.bantracker.enabled 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.

View File

@ -31,7 +31,8 @@ user = None
# Delete old sessions # Delete old sessions
try: 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: except:
pass pass
@ -41,7 +42,7 @@ if form.has_key('sess'):
if cookie.has_key('sess'): if cookie.has_key('sess'):
try: try:
sess = cookie['sess'].value 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] user = cur.fetchall()[0][0]
except: except:
con.commit() con.commit()
@ -54,7 +55,7 @@ if not user:
# Log # Log
if form.has_key('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() log = cur.fetchall()
con.commit() con.commit()
if form.has_key('mark'): if form.has_key('mark'):
@ -72,10 +73,10 @@ if form.has_key('log'):
# Main page # Main page
# Process comments # Process comments
if form.has_key('comment') and form.has_key('comment_id') and user: 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() comm = cur.fetchall()
if not len(comm): 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)))) (form['comment_id'].value,user,form['comment'].value,pickle.dumps(datetime.datetime.now(pytz.UTC))))
con.commit() con.commit()
@ -178,10 +179,10 @@ print '<th>Log</th></tr>'
# Select and filter bans # Select and filter bans
def getBans(id=None): def getBans(id=None):
if id == None: if id is None:
cur.execute("SELECT channel,mask,operator,time,removal,removal_op,id FROM bans ORDER BY id DESC") cur.execute('SELECT channel,mask,operator,time,removal,removal_op,id FROM bans ORDER BY id DESC')
else: 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() return cur.fetchall()
def myfilter(item, regex, kick, ban, oldban, mute, oldmute, floods, operator, channel): 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 ' class="bg2"'
print '>' print '>'
print '<td colspan="5" class="comment">' print '<td colspan="5" class="comment">'
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() comments = cur.fetchall()
if len(comments) == 0: if len(comments) == 0:
print '<span class="removal">(No comments) </span>' print '<span class="removal">(No comments) </span>'

View File

@ -1,7 +1,7 @@
# -*- Encoding: utf-8 -*- # -*- Encoding: utf-8 -*-
### ###
# Copyright (c) 2005-2007 Dennis Kaarsemaker # Copyright (c) 2005-2007 Dennis Kaarsemaker
# Copyright (c) 2008-2010 Terence Simpson # Copyright (c) 2008-2011 Terence Simpson
# Copyright (c) 2010 Elián Hanisch # Copyright (c) 2010 Elián Hanisch
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -55,9 +55,10 @@ def configure(advanced):
else: else:
return review 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?") enabled = yn("Enable Bantracker for all channels?")
database = something("Location of the Bantracker database", default=conf.supybot.directories.data.dirize('bans.db')) 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 don't want to run a web server)") 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) request = yn("Enable review and comment requests from bot?", default=False)
if request and advanced: if request and advanced:
@ -67,29 +68,31 @@ def configure(advanced):
type = set([]) type = set([])
for name in re.split(r',?\s+', types): for name in re.split(r',?\s+', types):
name = name.lower() name = name.lower()
if name in ('removal', 'ban', 'quiet'): if name in ValidTypes.validStrings:
type.add(name) type.add(name)
output("Which nicks should be bot not requets comments from?") output("Which nicks should be bot not requets comments from?")
output("Is case insensitive and wildcards '*' and '?' are accepted.") output("This is useful if you have automated channel bots that should not be directly asked for reviews")
ignores = anything("Separate types by spaces or commas:", default=', '.join(Bantracker.request.ignore._default)) 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([]) ignore = set([])
for name in re.split(r',?\s+', ignores): for name in re.split(r',?\s+', ignores):
name = name.lower() name = name.lower()
ignore.add(name) ignore.add(name)
output("You can set the comment and review requests for some nicks to be forwarded to specific nicks/channels") 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("Which nicks should these requests be forwarded for?")
output("Is case insensitive and wildcards '*' and '?' are accepted.") output("Is case-insensitive and the wildcards '*' and '?' are accepted.")
forwards = anything("Separate types by spaces or commas:", default=', '.join(Bantracker.request.forward._default)) forwards = anything("Separate nicks by spaces or commas:", default=', '.join(Bantracker.request.forward._default))
forward = set([]) forward = set([])
for name in re.split(r',?\s+', forwards): for name in re.split(r',?\s+', forwards):
name = name.lower() name = name.lower()
forward.add(name) forward.add(name)
output("Which nicks/channels should the requests be forwarded to?") output("Which nicks/channels should those requests be forwarded to?")
output("Is case insensitive and wildcards '*' and '?' are accepted.") output("Is case-insensitive and wildcards '*' and '?' are accepted.")
channels_i = anything("Separate types by spaces or commas:", default=', '.join(Bantracker.request.forward._default)) channels_i = anything("Separate nicks/channels by spaces or commas:", default=', '.join(Bantracker.request.forward._default))
channels = set([]) channels = set([])
for name in re.split(r',?\s+', channels_i): for name in re.split(r',?\s+', channels_i):
name = name.lower() name = name.lower()
@ -122,6 +125,7 @@ def configure(advanced):
Bantracker.database.setValue(db_file) Bantracker.database.setValue(db_file)
if os.path.exists(db_file): if os.path.exists(db_file):
output("%r already exists" % db_file)
return return
output("Creating an initial database in %r" % db_file) output("Creating an initial database in %r" % db_file)
@ -154,13 +158,6 @@ def configure(advanced):
user MEDIUMTEXT NOT NULL, user MEDIUMTEXT NOT NULL,
time INT NOT NULL time INT NOT NULL
)""") )""")
#"""
cur.execute("""CREATE TABLE users (
username VARCHAR(50) PRIMARY KEY,
salt VARCHAR(8),
password VARCHAR(50)
)""")
#""" #"""
except: except:
@ -176,7 +173,7 @@ Bantracker = conf.registerPlugin('Bantracker')
conf.registerChannelValue(Bantracker, 'enabled', conf.registerChannelValue(Bantracker, 'enabled',
registry.Boolean(False, """Enable the bantracker""")) registry.Boolean(False, """Enable the bantracker"""))
conf.registerGlobalValue(Bantracker, 'database', 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', conf.registerGlobalValue(Bantracker, 'bansite',
registry.String('', "Web site for the bantracker, without the 'bans.cgi' appended", private=True)) registry.String('', "Web site for the bantracker, without the 'bans.cgi' appended", private=True))

View File

@ -1,17 +1,8 @@
Copyright (c) 2005-2007, Dennis Kaarsemaker Bug information reporting plugin, works with many well-known bugtrackers.
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.
The syntax to add a tracker is weird, here are some examples: The syntax to add a tracker is weird, here are some examples:
@bugtracker add freedesktop bugzilla https://bugs.freedesktop.org Freedesktop @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 debian debbugs http://bugs.debian.org Debian
@bugtracker add openoffice issuezilla http://www.openoffice.org/issues OpenOffice @bugtracker add openoffice issuezilla http://www.openoffice.org/issues OpenOffice
@bugtracker add django trac http://code.djangoproject.com/ticket Django @bugtracker add django trac http://code.djangoproject.com/ticket Django
@ -21,21 +12,23 @@ In general: @bugtracker add <name> <type> <baseurl> [description]
Bugtracker dialects (types) this plugin understands: Bugtracker dialects (types) this plugin understands:
* Bugzilla * Bugzilla
* Issuezilla (OpenOffice.org's tjernobyl transformation of bugzilla) * Issuezilla (OpenOffice.org's tjernobyl transformation of bugzilla)
* Malone * Launchpad (Including Ubuntu)
* Debbugs (debbugs sucks donkeyballs - please fix debbugs) * Debbugs (debbugs sucks donkeyballs - please fix debbugs)
* Trac (with not-too-buggered-up templates, it needs to do screenscraping) * Trac
* 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 * str.php from the CUPS project
* Mantis (http://www.mantisbt.org) * 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: To request a bug report, use this syntax:
bug 123 bug 123
bug #123 bug #123
supybot bug 123 launchpad bug 123
bug 123, 4, 5 bug 123, 4, 5
bug 1, 3 and 89 bugs 1, 3 and 89
To rename a bugtracker: To rename a bugtracker:
@bugtracker rename old-name new-name @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 The bug snarfing (responding to bug numbers/urls) will only work in channels
where supybot.plugins.bugtracker.bugsnarfer is True. 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

View File

@ -1,7 +1,7 @@
# -*- Encoding: utf-8 -*- # -*- Encoding: utf-8 -*-
### ###
# Copyright (c) 2005-2007 Dennis Kaarsemaker # 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 # 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 # it under the terms of version 2 of the GNU General Public License as
@ -45,13 +45,14 @@ def configure(advanced):
else: else:
return repeatdelay 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) 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) 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) oopsSnarfer = yn("Enable detecting Launchpad OOPS IDs in all channels?", default=Bugtracker.oopsSnarfer._default)
if advanced: 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) 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) 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() repeatdelay = getRepeatdelay()
else: else:
replyNoBugtracker = Bugtracker.replyNoBugtracker._default replyNoBugtracker = Bugtracker.replyNoBugtracker._default
@ -89,8 +90,8 @@ conf.registerChannelValue(Bugtracker, 'oopsSnarfer',
enabled, such that any OOPS ### seen in the channel enabled, such that any OOPS ### seen in the channel
will have their information reported into the channel.""")) will have their information reported into the channel."""))
conf.registerChannelValue(Bugtracker, 'bugReporter', #conf.registerChannelValue(Bugtracker, 'bugReporter',
registry.String('', """Report new bugs (experimental)""")) # registry.String('', """Report new bugs (experimental)"""))
conf.registerChannelValue(Bugtracker, 'replyNoBugtracker', conf.registerChannelValue(Bugtracker, 'replyNoBugtracker',
registry.String('I don\'t have a bugtracker %s.', """Determines the phrase registry.String('I don\'t have a bugtracker %s.', """Determines the phrase
@ -98,7 +99,7 @@ conf.registerChannelValue(Bugtracker, 'replyNoBugtracker',
bugtracker site.""")) bugtracker site."""))
conf.registerChannelValue(Bugtracker, 'snarfTarget', 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""")) snarf command is triggered"""))
conf.registerGlobalValue(Bugtracker, 'bugtrackers', conf.registerGlobalValue(Bugtracker, 'bugtrackers',
@ -117,18 +118,18 @@ conf.registerChannelValue(Bugtracker, 'showassignee',
conf.registerChannelValue(Bugtracker, 'extended', conf.registerChannelValue(Bugtracker, 'extended',
registry.Boolean(False, "Show optional extneded bug information, specific to trackers")) registry.Boolean(False, "Show optional extneded bug information, specific to trackers"))
conf.registerGlobalValue(Bugtracker, 'reportercache', #conf.registerGlobalValue(Bugtracker, 'reportercache',
registry.String('', """Name of the basedir for the bugreporter cache""", private=True)) # registry.String('', """Name of the basedir for the bugreporter cache""", private=True))
conf.registerGlobalValue(Bugtracker, 'imap_server', #conf.registerGlobalValue(Bugtracker, 'imap_server',
registry.String('', """IMAP server for bugmail account""",private=True)) # registry.String('', """IMAP server for bugmail account""",private=True))
conf.registerGlobalValue(Bugtracker, 'imap_user', #conf.registerGlobalValue(Bugtracker, 'imap_user',
registry.String('', """IMAP user for bugmail account""", private=True)) # registry.String('', """IMAP user for bugmail account""", private=True))
conf.registerGlobalValue(Bugtracker, 'imap_password', #conf.registerGlobalValue(Bugtracker, 'imap_password',
registry.String('', """IMAP password for bugmail account""", private=True)) # registry.String('', """IMAP password for bugmail account""", private=True))
conf.registerGlobalValue(Bugtracker, 'imap_ssl', #conf.registerGlobalValue(Bugtracker, 'imap_ssl',
registry.Boolean(False, """Use SSL for imap connections""", private=True)) # registry.Boolean(False, """Use SSL for imap connections""", private=True))

View File

@ -24,13 +24,15 @@ import supybot.registry as registry
import supybot.schedule as schedule import supybot.schedule as schedule
import supybot.log as supylog import supybot.log as supylog
import re, os, time, imaplib, commands #import imaplib
import re, os, time, commands
import xml.dom.minidom as minidom import xml.dom.minidom as minidom
from htmlentitydefs import entitydefs as entities from htmlentitydefs import entitydefs as entities
import email.FeedParser import email.FeedParser
import SOAPpy 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): def makeClean(s):
words = s.split() words = s.split()
@ -95,7 +97,7 @@ class Bugtracker(callbacks.PluginRegexp):
def __init__(self, irc): def __init__(self, irc):
callbacks.PluginRegexp.__init__(self, irc) callbacks.PluginRegexp.__init__(self, irc)
self.db = ircutils.IrcDict() self.db = ircutils.IrcDict()
events = [] # self.events = []
for name in self.registryValue('bugtrackers'): for name in self.registryValue('bugtrackers'):
registerBugtracker(name) registerBugtracker(name)
group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False) group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False)
@ -104,27 +106,28 @@ class Bugtracker(callbacks.PluginRegexp):
else: else:
self.log.warning("Bugtracker: Unknown trackertype: %s (%s)" % (group.trackertype(), name)) self.log.warning("Bugtracker: Unknown trackertype: %s (%s)" % (group.trackertype(), name))
self.shorthand = utils.abbrev(self.db.keys()) self.shorthand = utils.abbrev(self.db.keys())
# Schedule bug reporting
self.shown = {} self.shown = {}
#TODO: Remove everything below this line
if self.registryValue('imap_server') and self.registryValue('reportercache'): # # Schedule bug reporting
try: # #TODO: Remove everything below this line
schedule.removeEvent(self.name() + '.bugreporter') # if self.registryValue('imap_server') and self.registryValue('reportercache'):
except: # try:
pass # schedule.removeEvent(self.name() + '.bugreporter')
schedule.addPeriodicEvent(lambda: self.reportnewbugs(irc), 60, name=self.name() + '.bugreporter') # except:
self.events += [self.name() + '.bugreporter'] # pass
self.log.info('Bugtracker: Adding scheduled event "%s.bugreporter"' % self.name()) # 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 def die(self): #TODO: Remove me
try: pass
for event in self.events: # try:
self.log.info('Bugtracker: Removing scheduled event "%s"' % event) # for event in self.events:
schedule.removeEvent(event) # self.log.info('Bugtracker: Removing scheduled event "%s"' % event)
schedule.removeEvent(self.name()) # schedule.removeEvent(event)
except: # schedule.removeEvent(self.name())
pass # except:
# pass
def is_ok(self, channel, tracker, bug): def is_ok(self, channel, tracker, bug):
'''Flood/repeat protection''' '''Flood/repeat protection'''
@ -138,95 +141,97 @@ class Bugtracker(callbacks.PluginRegexp):
return False return False
def is_new(self, tracker, tag, id): #Depricated def is_new(self, tracker, tag, id): #Depricated
bugreporter_base = self.registryValue('reportercache') pass
if not os.path.exists(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id))): # bugreporter_base = self.registryValue('reportercache')
try: # if not os.path.exists(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id))):
os.makedirs(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)))) # try:
except: # os.makedirs(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000))))
pass # except:
fd = open(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id)),'w') # pass
fd.close() # fd = open(os.path.join(bugreporter_base,tag,tracker.name,str(int(id/1000)),str(id)),'w')
return True # fd.close()
return False # return True
# return False
def reportnewbugs(self,irc): #Depricated def reportnewbugs(self,irc): #Depricated
# Compile list of bugs pass
self.log.info("Bugtracker: Checking for new bugs") # # Compile list of bugs
bugs = {} # self.log.info("Bugtracker: Checking for new bugs")
if self.registryValue('imap_ssl'): # bugs = {}
sc = imaplib.IMAP4_SSL(self.registryValue('imap_server')) # if self.registryValue('imap_ssl'):
else: # sc = imaplib.IMAP4_SSL(self.registryValue('imap_server'))
sc = imaplib.IMAP4(self.registryValue('imap_server')) # else:
sc.login(self.registryValue('imap_user'), self.registryValue('imap_password')) # sc = imaplib.IMAP4(self.registryValue('imap_server'))
sc.select('INBOX') # sc.login(self.registryValue('imap_user'), self.registryValue('imap_password'))
new_mail = sc.search(None, '(UNSEEN)')[1][0].split()[:20] # sc.select('INBOX')
# new_mail = sc.search(None, '(UNSEEN)')[1][0].split()[:20]
# Read all new mail #
for m in new_mail: # # Read all new mail
msg = sc.fetch(m, 'RFC822')[1][0][1] # for m in new_mail:
fp = email.FeedParser.FeedParser() # msg = sc.fetch(m, 'RFC822')[1][0][1]
sc.store(m, '+FLAGS', "(\Deleted)") # Mark message deleted so we don't have to process it again # fp = email.FeedParser.FeedParser()
fp.feed(msg) # sc.store(m, '+FLAGS', "(\Deleted)") # Mark message deleted so we don't have to process it again
bug = fp.close() # fp.feed(msg)
tag = None # 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)') # if 'X-Launchpad-Bug' not in bug.keys():
continue # self.log.info('Bugtracker: Ignoring e-mail with no detectable bug (Not from Launchpad)')
else: # continue
tag = bug['X-Launchpad-Bug'] # else:
if 'distribution=' not in tag and 'product=' not in tag: # tag = bug['X-Launchpad-Bug']
self.log.info('Bugtracker: Ignoring e-mail with no detectable bug (no distro/product)') # if 'distribution=' not in tag and 'product=' not in tag:
continue # self.log.info('Bugtracker: Ignoring e-mail with no detectable bug (no distro/product)')
else: # continue
tag = tag.split(';')[0].strip().replace("product=",'').replace("distribution=","") # 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)') # 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: # tag = tag[tag.find('+')+1:tag.find('@')]
bugs[tag] = {} # if tag not in bugs:
# bugs[tag] = {}
# Determine bugtracker type (currently only Launchpad is supported anyway) #
if bug['X-Launchpad-Bug']: # # Determine bugtracker type (currently only Launchpad is supported anyway)
tracker = self.db['launchpad'] # if bug['X-Launchpad-Bug']:
id = int(bug['Reply-To'].split()[1]) # tracker = self.db['launchpad']
subj = bug['Subject']; # id = int(bug['Reply-To'].split()[1])
if '[NEW]' not in subj: #Not a new bug # subj = bug['Subject'];
continue # if '[NEW]' not in subj: #Not a new bug
if self.is_new(tracker, tag, id): # continue
component = bug['X-Launchpad-Bug'] # if self.is_new(tracker, tag, id):
if 'component' in component: # component = bug['X-Launchpad-Bug']
component = component[component.find('component=')+10:] # if 'component' in component:
component = component[:component.find(';')].replace('None','') # component = component[component.find('component=')+10:]
else: # component = component[:component.find(';')].replace('None','')
component = '' # else:
try: # component = ''
if component: # try:
bugs[tag][id] = self.get_bug('',tracker, id, False)[0].replace('"','(%s) "' % component, 1) # if component:
else: # bugs[tag][id] = self.get_bug('',tracker, id, False)[0].replace('"','(%s) "' % component, 1)
bugs[tag][id] = self.get_bug('',tracker, id, False)[0] # else:
if '[apport]' in bugs[tag][id]: # bugs[tag][id] = self.get_bug('',tracker, id, False)[0]
bugs[tag].pop(id) # if '[apport]' in bugs[tag][id]:
except: # bugs[tag].pop(id)
self.log.info("Bugtracker: Unable to get new bug %d" % id) # except:
pass # self.log.info("Bugtracker: Unable to get new bug %d" % id)
else: # pass
self.log.info('Bugtracker: Ignoring e-mail with no detectable bug') # else:
# self.log.info('Bugtracker: Ignoring e-mail with no detectable bug')
reported_bugs = 0 #
# reported_bugs = 0
for c in irc.state.channels: #
tags = self.registryValue('bugReporter', channel=c) # for c in irc.state.channels:
if not tags: # tags = self.registryValue('bugReporter', channel=c)
continue # if not tags:
for tag in tags.split(','): # continue
if not tag or tag not in bugs.keys(): # for tag in tags.split(','):
continue # if not tag or tag not in bugs.keys():
for b in sorted(bugs[tag].keys()): # continue
irc.queueMsg(ircmsgs.privmsg(c,'New bug: #%s' % bugs[tag][b][bugs[tag][b].find('bug ')+4:])) # for b in sorted(bugs[tag].keys()):
reported_bugs = reported_bugs+1 # 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): def add(self, irc, msg, args, name, trackertype, url, description):
"""<name> <type> <url> [<description>] """<name> <type> <url> [<description>]
@ -359,18 +364,18 @@ class Bugtracker(callbacks.PluginRegexp):
msg.tag('nbugs', nbugs + len(bugids)) msg.tag('nbugs', nbugs + len(bugids))
bt = map(lambda x: x.lower(), match.group('bt').split()) bt = map(lambda x: x.lower(), match.group('bt').split())
# Strip off trailing ':' from the tracker name. Allows for (LP: #nnnnnn) # 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] bt[0] = bt[:-1]
name = '' name = ''
if len(bt) == 1 and not (bt[0] in ['bug','bugs']): if len(bt) == 1 and not (bt[0] in ['bug','bugs']):
try: try:
name = bt[0].lower() name = bt[0]
tracker = self.db[name] tracker = self.db[name]
except: except:
return return
elif len(bt) == 2: elif len(bt) == 2:
try: try:
name = bt[0].lower() name = bt[0]
tracker = self.db[name] tracker = self.db[name]
except: except:
name = '' name = ''

10
COPYING
View File

@ -1,10 +1,10 @@
Most of the code in this package is licensed under the GNU GPL Version 2, Unless otherwise specified, the code in this package is licensed under the
the exception is the Lart plugin, which has a BSD-style license. GNU GPL Version 2 license, Refer to each of the files in plugin directories
See Lart/__init__.py for the license. for specifics.
NOTE! The GPL below is copyrighted by the Free Software Foundation, but 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 the instance of code that it refers to (the plugins) are copyrighted
by the authors who actually wrote it. by the authors who actually wrote them.
--------------------------------------------------------------------------- ---------------------------------------------------------------------------

View File

@ -1,23 +1,16 @@
Copyright (c) 2006-2007, Dennis Kaarsemaker Factoid plugin
Note: This plugin used to have package lookup, this was mooved to the
This program is free software; you can redistribute it and/or modify PackageInfo plugin.
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.
Pick a name for your database. A lowercase-only name without spaces is probably 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 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 your databases in (somewere in $botdir/data would be best).
create an sqlite database with the following command: 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 sqlite myfactoids.db
Then copy/paste in the below 2 tables:
CREATE TABLE facts ( CREATE TABLE facts (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
@ -36,6 +29,7 @@ CREATE TABLE log (
oldvalue VARCHAR(200) NOT NULL oldvalue VARCHAR(200) NOT NULL
); );
If you want to create more databases, repeat these last two steps. 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. 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). the database (without the .db suffix).
Documentation on adding/editing factoids can be found on Documentation on adding/editing factoids can be found on
https://wiki.ubuntu.com/UbuntuBots To give people edit access, let them register https://ubottu.com/devel/wiki/Plugins#Encyclopedia
with your bot and use: %addeditor nickname_here (replace % with your prefix To give people edit access, let them register with your bot and use the command:
char). Similarly you can use removeeditor :). @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 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 commoncgi.py file from the bzr tree. Make sure you set the variables datadir and

View File

@ -105,12 +105,12 @@ def configure(advanced):
db_file = Encyclopedia.database() db_file = Encyclopedia.database()
if not db_dir: 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) output("supybot.plugins.Encyclopedia.datadir will be set to %r" % db_dir)
Encyclopedia.datadir.setValue(db_dir) Encyclopedia.datadir.setValue(db_dir)
if not db_file: if not db_file:
db_file = 'ubuntu' db_file = Encyclopedia.database._default
output("supybot.plugins.Encyclopedia.database will be set to %r" % db_file) output("supybot.plugins.Encyclopedia.database will be set to %r" % db_file)
Encyclopedia.database.setValue(db_dir) Encyclopedia.database.setValue(db_dir)

View File

@ -31,13 +31,14 @@ else:
import sre as re import sre as re
def checkIgnored(hostmask, recipient='', users=ircdb.users, channels=ircdb.channels): def checkIgnored(hostmask, recipient='', users=ircdb.users, channels=ircdb.channels):
if ircdb.ignores.checkIgnored(hostmask):
return True
try: try:
id = ircdb.users.getUserId(hostmask) id = ircdb.users.getUserId(hostmask)
user = users.getUser(id) user = users.getUser(id)
except KeyError: except KeyError:
# If there's no user... # If there's no user...
if ircdb.ignores.checkIgnored(hostmask):
return True
if ircutils.isChannel(recipient): if ircutils.isChannel(recipient):
channel = channels.getChannel(recipient) channel = channels.getChannel(recipient)
if channel.checkIgnored(hostmask): if channel.checkIgnored(hostmask):
@ -46,9 +47,13 @@ def checkIgnored(hostmask, recipient='', users=ircdb.users, channels=ircdb.chann
return False return False
else: else:
return False return False
if user._checkCapability('owner'): if user._checkCapability('owner'):
# Owners shouldn't ever be ignored. # Owners shouldn't ever be ignored.
return False return False
if ircdb.ignores.checkIgnored(hostmask):
return True
elif user.ignore: elif user.ignore:
return True return True
elif recipient: elif recipient:

View File

@ -1,22 +1,26 @@
This plugin allows package lookup via apt-cache/apt-file 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-- --Setup--
supybot.plugins.PackageInfo.aptdir: supybot.plugins.PackageInfo.aptdir:
Directory to use to store the apt cache (Global) 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. 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 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 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 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. The .list file should contain _both_ the deb and deb-src source lines.
Eg: Eg; in lucid.list:
deb http://archive.ubuntu.com/ubuntu jaunty main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu ludid main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu jaunty main restricted universe multiverse deb-src http://archive.ubuntu.com/ubuntu lucid main restricted universe multiverse
supybot.plugins.PackageInfo.defaultRelease: supybot.plugins.PackageInfo.defaultRelease:
Set this to the default release to use when none is specified. (Channel) 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 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, and update_apt_file scripts that comes with this plugin. Before you run these,

View File

@ -43,17 +43,17 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
if enabled and advanced: if enabled and advanced:
prefixchar = something("Which prefix character should be bot respond to?", default=PackageInfo.prefixchar._default) 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) 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 # 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") 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: else:
prefixchar = PackageInfo.prefixchar._default prefixchar = PackageInfo.prefixchar._default
defaultRelease = PackageInfo.defaultRelease._default defaultRelease = PackageInfo.defaultRelease._default
aptdir = conf.supybot.directories.data.dirize('aptdir') aptdir = PackageInfo.aptdir._default
PackageInfo.enabled.setValue(enabled) 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.prefixchar.setValue(prefixchar)
PackageInfo.defaultRelease.setValue(defaultRelease) 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__)) pluginDir = os.path.abspath(os.path.dirname(__file__))
update_apt = os.path.join(pluginDir, 'update_apt') update_apt = os.path.join(pluginDir, 'update_apt')
update_apt_file = os.path.join(pluginDir, 'update_apt_file') update_apt_file = os.path.join(pluginDir, 'update_apt_file')
@ -115,7 +115,7 @@ conf.registerChannelValue(PackageInfo, 'enabled',
conf.registerChannelValue(PackageInfo, 'prefixchar', conf.registerChannelValue(PackageInfo, 'prefixchar',
conf.ValidPrefixChars('!', "Character the bot will respond to")) conf.ValidPrefixChars('!', "Character the bot will respond to"))
conf.registerChannelValue(PackageInfo, 'defaultRelease', 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.registerGlobalValue(PackageInfo, 'aptdir',
conf.Directory(conf.supybot.directories.data.dirize('aptdir'), "Path to the apt directory", private=True)) conf.Directory(conf.supybot.directories.data.dirize('aptdir'), "Path to the apt directory", private=True))

5
PackageInfo/update_apt Normal file → Executable file
View File

@ -1,11 +1,14 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Set DIR to the same value as supybot.plugins.PackageInfo.aptdir
if [ -z "$DIR" ]; then if [ -z "$DIR" ]; then
DIR=/home/bot/aptdir DIR=/home/bot/aptdir
fi fi
# Be quiet bt default
DEFAULT_OPTS="-qq" DEFAULT_OPTS="-qq"
# Check command-line arguments
while [ "x$1" != "x" ]; do while [ "x$1" != "x" ]; do
case "$1" in case "$1" in
-v|--verbose) -v|--verbose)
@ -27,7 +30,7 @@ while [ "x$1" != "x" ]; do
exit 1 exit 1
;; ;;
*) *)
echo "This script takes no arguments" >&2 echo "This script takes no non-argument parameters" >&2
exit 1 exit 1
;; ;;
esac esac

5
PackageInfo/update_apt_file Normal file → Executable file
View File

@ -5,14 +5,17 @@ if [ ! -x "$(which apt-file)" ]; then
exit 1 exit 1
fi fi
# Set DIR to the same value as supybot.plugins.PackageInfo.aptdir
if [ -z "$DIR" ]; then if [ -z "$DIR" ]; then
DIR=/home/bot/aptdir DIR=/home/bot/aptdir
fi fi
# Be quiet by default
if [ -z "$VERBOSE" ]; then if [ -z "$VERBOSE" ]; then
VERBOSE="no" VERBOSE="no"
fi fi
# Check command-line arguments
while [ "x$1" != "x" ]; do while [ "x$1" != "x" ]; do
case "$1" in case "$1" in
-v|--verbose) -v|--verbose)
@ -31,7 +34,7 @@ while [ "x$1" != "x" ]; do
exit 1 exit 1
;; ;;
*) *)
echo "This script takes no arguments" >&2 echo "This script takes no non-argument parameterss" >&2
exit 1 exit 1
;; ;;
esac esac

View File

@ -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 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 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 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 there and rename it to "plugins". Alternatively you can create an empty plugins
following installed on the system: 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 Name Debian/Ubuntu package Website
Python-apt python-apt N/A Debian (and derivatives) only 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 Launchpadlib python-launchpadlib https://launchpad.net/launchpadlib
apt-file apt-file N/A Debian (and derivatives) only 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 Once these packages are installed, and in the bot directory containing the
"plugins" sub-directory, run this command: "supybot-wizard". "plugins" sub-directory, run this command: "supybot-wizard".
This wizard will guide you through the process of setting up the bot for an IRC 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. Bugtracker Show information on bugs for various bug trackers.
Encyclopedia A factoid encyclopaedia. Encyclopedia A factoid encyclopaedia.
IRCLogin Allows password-less login from users identified to services. IRCLogin Allows password-less login from users identified to services.
Lart A database of "larts". Lart A database of "larts". (Unmaintained)
Mess Random mess, pulls random things from the internet. Mess Random mess, pulls random things from the internet. (Unmaintained)
PackageInfo Lookup information on Debian packages and file search. PackageInfo Lookup information on Debian packages and file search.
(works on Debian and derivatives only) (works on Debian and derivatives only, unless you take special measures)
Webcal Updates a channel topic based on events in an iCal. Webcal Updates a channel topic based on events in an iCal. (Unmaintained)
Note: Mess and Lart are largely unmaintained but are working, Webcal is Note: Mess and Lart are largely unmaintained but are working, Webcal is
unmaintained and mostly broken except for extremely simple iCal feeds. 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 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 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 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 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 the bot in IRC.
be created in "data/aptdir/", these will be used with the "apt-cache" and The Bantracker database from ubottu is not available to the
"apt-file" commands to retrieve package information and find files within public, as it will contain logs of channels which are not publically logged.
packages. When asked if you want to run the "update_apt" script you should
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 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. 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 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 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 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 set it to anything other than the prefix character for Encyclopedia and
PacakgeInfo. If you weren't asked, it defaults to '!'. The recommended PacakgeInfo. If you weren't asked, it defaults to '!' for those plugins. The
character is '@'. 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.

View File

@ -31,6 +31,7 @@ if cookie.has_key('tz'):
cookie['tz']['version'] = 1 cookie['tz']['version'] = 1
class IOWrapper: class IOWrapper:
'''Class to wrap default IO, used with templates'''
def __init__(self): def __init__(self):
self.buf = [] self.buf = []
def write(self, val): def write(self, val):
@ -42,6 +43,7 @@ sys.stdout = IOWrapper()
sys.stderr = IOWrapper() sys.stderr = IOWrapper()
def send_page(template): def send_page(template):
'''Sends a template page and exit'''
data = sys.stdout.getvalue() data = sys.stdout.getvalue()
errdata = sys.stderr.getvalue() errdata = sys.stderr.getvalue()
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
@ -64,5 +66,6 @@ def send_page(template):
sys.exit(0) sys.exit(0)
def q(txt): def q(txt):
'''Simple HTML entity quoting'''
return txt.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('"','&quot;') return txt.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('"','&quot;')