diff --git a/.gitignore b/.gitignore
index c78c5d6..fde6af8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,8 @@
-karma*
*.facts
*.pyc
*.swp
*.urls
*~
+dr.botzo.data
dr.botzo.cfg
nbproject
diff --git a/Module.py b/Module.py
index aaec8f5..90c701c 100644
--- a/Module.py
+++ b/Module.py
@@ -17,9 +17,11 @@ along with this program. If not, see .
"""
from ConfigParser import NoSectionError, NoOptionError
+
import inspect
import re
import sys
+import sqlite3
from extlib import irclib
@@ -281,6 +283,39 @@ class Module(object):
self.server._handle_event(event)
+ def get_db(self):
+ """
+ Get a database connection to sqlite3. Once grabbed, it should be closed
+ when work is done. Modules that need a database connection should
+ test for and create (or, eventually, alter) required table structure
+ in their __init__ IF that structure does not already exist. Well-behaved
+ modules should use a prefix in their table names (eg "karma_log" rather
+ than "log")
+
+ See also db_module_registered, below.
+ """
+
+ dbfile = self.config.get('dr.botzo', 'database')
+ conn = sqlite3.connect(dbfile)
+ return conn
+
+ def db_module_registered(self, modulename):
+ """
+ ask the database for a version number for a module. Return that version
+ number if the module has registered, or None if not
+ """
+ conn = self.get_db()
+ version = None
+ try:
+ cur = conn.cursor()
+ cur.execute("SELECT version FROM drbotzo_modules WHERE module = :name",
+ {'name': modulename})
+ version = cur.fetchone()
+ if (version != None):
+ version = version[0]
+ finally: conn.close()
+ return version
+
def do(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
"""
Do the primary thing this module was intended to do.
diff --git a/dr.botzo.cfg.example b/dr.botzo.cfg.example
index 7bf599d..efa4417 100644
--- a/dr.botzo.cfg.example
+++ b/dr.botzo.cfg.example
@@ -7,10 +7,10 @@ usermode = -x
debug = true
admin_userhost = bss@ayu.incorporeal.org
module_list = IrcAdmin
+database = dr.botzo.data
[IrcAdmin]
autojoin = #bss
[Karma]
-meta.pubmsg_needs_bot_prefix = false
-karmafile = karma
\ No newline at end of file
+meta.pubmsg_needs_bot_prefix = false
\ No newline at end of file
diff --git a/dr.botzo.py b/dr.botzo.py
index b436cab..f153b3c 100644
--- a/dr.botzo.py
+++ b/dr.botzo.py
@@ -22,6 +22,7 @@ import re
import socket
import sys
import inspect
+import sqlite3
from extlib import irclib
@@ -103,6 +104,32 @@ except NoOptionError as e:
# load additional options
irclib.DEBUG = config.getboolean('dr.botzo', 'debug')
+try:
+ # make sure we can initialize the database, if such a thing is
+ # called for in the config file
+ dbfile = config.get('dr.botzo', 'database')
+ conn = sqlite3.connect(dbfile)
+ try:
+ query = """
+ SELECT COUNT(*)
+ FROM sqlite_master
+ WHERE type = 'table' AND name = 'drbotzo_modules'
+ """
+ row = conn.execute(query).fetchone()
+ if row[0] == 0:
+ # need to create the drbotzo_modules table
+ query = """
+ CREATE TABLE drbotzo_modules (
+ module TEXT,
+ version INTEGER
+ )
+ """
+ conn.execute(query)
+ conn.commit()
+ finally: conn.close()
+except NoOptionError: pass # if the config file has no db property, assume that
+except NoSectionError: pass # the database doesn't need to exist
+
# start up the IRC bot
# create IRC and server objects and connect
diff --git a/modules/Karma.py b/modules/Karma.py
index 784ca00..1c62f10 100755
--- a/modules/Karma.py
+++ b/modules/Karma.py
@@ -17,7 +17,6 @@ along with this program. If not, see .
"""
import re
-import shelve
from Module import Module
@@ -33,18 +32,41 @@ class Karma(Module):
Module.__init__(self, config, server, modlist)
- filename = self.config.get(self.__class__.__name__, 'karmafile')
- self.karmafile = filename + "_karma.dat"
- self.trendfile = filename + "_trends.dat"
-
pattern = "(?:([a-zA-Z0-9_']+)|\(([a-zA-Z0-9_' ]+)\))"
-
karmapattern = '^' + pattern + '(\+\+|--)'
querypattern = '^!rank\s+(.*)'
self.karmare = re.compile(karmapattern)
self.queryre = re.compile(querypattern)
+ # need to init the database if karma tables don't already exist
+ version = self.db_module_registered(self.__class__.__name__)
+ if (version == None):
+ # have to create the database tables
+ conn = self.get_db()
+ try:
+ conn.execute('''
+ CREATE TABLE karma_log (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ key TEXT NOT NULL,
+ delta INTEGER NOT NULL,
+ who TEXT NOT NULL,
+ userhost TEXT NOT NULL,
+ karmatime TEXT DEFAULT CURRENT_TIMESTAMP
+ )''')
+ conn.execute('CREATE INDEX karma_log_key_ix ON karma_log (key)')
+ conn.execute('CREATE INDEX karma_log_who_ix ON karma_log (who)')
+ conn.execute('''
+ CREATE VIEW karma_values AS
+ SELECT key, SUM(delta) AS value
+ FROM karma_log
+ GROUP BY key''')
+
+ sql = 'INSERT INTO drbotzo_modules VALUES (?,?)'
+ conn.execute(sql, (self.__class__.__name__, 1))
+ conn.commit()
+ finally: conn.close()
+
def do(self, connection, event, nick, userhost, replypath, what, admin_unlocked):
"""look for karma strings at the start of messages"""
@@ -66,51 +88,41 @@ class Karma(Module):
else:
value = -1;
- # do karma recording
- karma = shelve.open(self.karmafile)
+ conn = self.get_db()
try:
- oldvalue = 0;
- if karma.has_key(key):
- oldvalue = karma[key]
- newvalue = oldvalue + value;
- karma[key] = newvalue;
- finally:
- karma.close()
-
- trend = shelve.open(self.trendfile)
- try:
- nickpos = nick + "_pos"
- nickneg = nick + "_neg"
- trend_pos = 0;
- trend_neg = 0;
- if trend.has_key(nickpos):
- trend_pos = trend[nickpos]
- if trend.has_key(nickneg):
- trend_neg = trend[nickneg]
- if value > 0:
- trend_pos = trend_pos + 1
- else:
- trend_neg = trend_neg + 1
- trend[nickpos] = trend_pos;
- trend[nickneg] = trend_neg;
- finally:
- trend.close();
-
- reply = "karma change for '" + key + "' (" + str(value) + ") by " + nick
- self.reply(connection, replypath, reply)
+ sql = '''
+ INSERT INTO karma_log (key, delta, who, userhost)
+ VALUES (?, ?, ?, ?)
+ '''
+ conn.execute(sql, (key, value, nick, userhost))
+ conn.commit()
+ finally: conn.close()
def handle_karma_query(self, connection, nick, userhost, replypath, what):
match = self.queryre.match(what)
key = match.group(1)
- karma = shelve.open(self.karmafile, "r")
- reply = key + ' has no karma'
+ conn = self.get_db()
+ reply = '{nick}: {key} has no karma'.format(nick=nick, key=key)
try:
- if karma.has_key(key):
- value = karma[key]
- reply = key + ' has ' + str(value) + ' points of karma'
- finally:
- karma.close()
+ query = '''
+ SELECT value
+ FROM karma_values
+ WHERE key = :key
+ '''
+ value = conn.execute(query, {'key': key}).fetchone()
+
+ if (value != None):
+ query = '''
+ SELECT count(*) FROM karma_values WHERE value > :value
+ '''
+ rank = conn.execute(query, {'value': value[0]}).fetchone()
+ rank = rank[0] + 1;
+
+ reply = '{nick}: {key} has {value[0]!s} points of karma (rank {rank})'.format(
+ nick=nick, key=key, value=value, rank=rank)
+
+ finally: conn.close()
self.reply(connection, replypath, reply)