diff --git a/plugins/QuoteGrabs.py b/plugins/QuoteGrabs.py
index 53e7610dd..ceb9d6841 100644
--- a/plugins/QuoteGrabs.py
+++ b/plugins/QuoteGrabs.py
@@ -37,28 +37,21 @@ as see who "grabbed" the quote in the first place.
__revision__ = "$Id$"
-import supybot.plugins as plugins
-
import os
import time
import random
import supybot.registry as registry
+import supybot.dbi as dbi
import supybot.conf as conf
import supybot.ircdb as ircdb
import supybot.utils as utils
+from supybot.commands import *
import supybot.ircmsgs as ircmsgs
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
-from supybot.commands import wrap, additional
-
-try:
- import sqlite
-except ImportError:
- raise callbacks.Error, 'You need to have PySQLite installed to use this ' \
- 'plugin. Download it at '
conf.registerPlugin('QuoteGrabs')
conf.registerChannelValue(conf.supybot.plugins.QuoteGrabs, 'randomGrabber',
@@ -80,31 +73,141 @@ conf.registerChannelValue(conf.supybot.plugins.QuoteGrabs.randomGrabber,
minimum number of characters in a message for it to be considered for
random grabbing."""))
-class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
- def __init__(self):
- plugins.ChannelDBHandler.__init__(self)
- callbacks.Privmsg.__init__(self)
+class QuoteGrabsRecord(dbi.Record):
+ __fields__ = [
+ 'by',
+ 'text',
+ 'grabber',
+ 'at',
+ 'hostmask',
+ ]
- def makeDb(self, filename):
+ def __str__(self):
+ at = time.strftime(conf.supybot.humanTimestampFormat(),
+ time.localtime(float(self.at)))
+ grabber = plugins.getUserName(self.grabber)
+ return '%s (Said by: %s; grabbed by %s at %s)' % \
+ (self.text, self.hostmask, grabber, at)
+
+class SqliteQuoteGrabsDB(object):
+ def __init__(self, filename):
+ self.dbs = ircutils.IrcDict()
+ self.filename = filename
+
+ def close(self):
+ for db in self.dbs.itervalues():
+ db.close()
+
+ def _getDb(self, channel):
+ try:
+ import sqlite
+ except ImportError:
+ raise callbacks.Error, 'You need to have PySQLite installed to ' \
+ 'use this plugin. Download it at ' \
+ ''
+ filename = plugins.makeChannelFilename(self.filename, channel)
+ if filename in self.dbs:
+ return self.dbs[filename]
if os.path.exists(filename):
- db = sqlite.connect(filename, converters={'bool': bool})
- else:
- db = sqlite.connect(filename, coverters={'bool': bool})
- cursor = db.cursor()
- cursor.execute("""CREATE TABLE quotegrabs (
- id INTEGER PRIMARY KEY,
- nick TEXT,
- hostmask TEXT,
- added_by TEXT,
- added_at TIMESTAMP,
- quote TEXT
- );""")
+ self.dbs[filename] = sqlite.connect(filename,
+ converters={'bool': bool})
+ return self.dbs[filename]
+ db = sqlite.connect(filename, converters={'bool': bool})
+ self.dbs[filename] = db
+ cursor = db.cursor()
+ cursor.execute("""CREATE TABLE quotegrabs (
+ id INTEGER PRIMARY KEY,
+ nick TEXT,
+ hostmask TEXT,
+ added_by TEXT,
+ added_at TIMESTAMP,
+ quote TEXT
+ );""")
def p(s1, s2):
return int(ircutils.nickEqual(s1, s2))
db.create_function('nickeq', 2, p)
db.commit()
return db
+ def get(self, channel, id):
+ db = self._getDb(channel)
+ cursor = db.cursor()
+ cursor.execute("""SELECT id, nick, quote, hostmask, added_at, added_by
+ FROM quotegrabs WHERE id = %s""", id)
+ if cursor.rowcount == 0:
+ raise dbi.NoRecordError
+ (id, by, quote, hostmask, at, grabber) = cursor.fetchone()
+ return QuoteGrabsRecord(id, by=by, text=quote, hostmask=hostmask,
+ at=at, grabber=grabber)
+
+ def random(self, channel, nick):
+ db = self._getDb(channel)
+ cursor = db.cursor()
+ if nick:
+ cursor.execute("""SELECT quote FROM quotegrabs
+ WHERE nickeq(nick, %s)
+ ORDER BY random() LIMIT 1""",
+ nick)
+ else:
+ cursor.execute("""SELECT quote FROM quotegrabs
+ ORDER BY random() LIMIT 1""")
+ if cursor.rowcount == 0:
+ raise dbi.NoRecordError
+ return cursor.fetchone()[0]
+
+ def list(self, channel, nick):
+ db = self._getDb(channel)
+ cursor = db.cursor()
+ cursor.execute("""SELECT id, quote FROM quotegrabs
+ WHERE nickeq(nick, %s)
+ ORDER BY id ASC""", nick)
+ return [QuoteGrabsRecord(id, text=quote)
+ for (id, quote) in cursor.fetchall()]
+
+ def getQuote(self, channel, nick):
+ db = self._getDb(channel)
+ cursor = db.cursor()
+ cursor.execute("""SELECT quote FROM quotegrabs
+ WHERE nickeq(nick, %s)
+ ORDER BY id DESC LIMIT 1""", nick)
+ if cursor.rowcount == 0:
+ raise dbi.NoRecordError
+ return cursor.fetchone()[0]
+
+ def select(self, channel, nick):
+ db = self._getDb(channel)
+ cursor = db.cursor()
+ cursor.execute("""SELECT added_at FROM quotegrabs
+ WHERE nickeq(nick, %s)
+ ORDER BY id DESC LIMIT 1""", nick)
+ if cursor.rowcount == 0:
+ raise dbi.NoRecordError
+
+ def add(self, msg, by):
+ channel = msg.args[0]
+ db = self._getDb(channel)
+ cursor = db.cursor()
+ text = ircmsgs.prettyPrint(msg)
+ # Check to see if the latest quotegrab is identical
+ cursor.execute("""SELECT quote FROM quotegrabs
+ WHERE nick=%s
+ ORDER BY id DESC LIMIT 1""", msg.nick)
+ if cursor.rowcount != 0:
+ if text == cursor.fetchone()[0]:
+ return
+ cursor.execute("""INSERT INTO quotegrabs
+ VALUES (NULL, %s, %s, %s, %s, %s)""",
+ msg.nick, msg.prefix, by, int(time.time()), text)
+ db.commit()
+
+QuoteGrabsDB = plugins.DB('QuoteGrabs', {'sqlite': SqliteQuoteGrabsDB})
+
+class QuoteGrabs(callbacks.Privmsg):
+ def __init__(self):
+ self.__parent = super(QuoteGrabs, self)
+ self.__parent.__init__()
+ self.db = QuoteGrabsDB()
+
def doPrivmsg(self, irc, msg):
irc = callbacks.SimpleProxy(irc, msg)
if ircutils.isChannel(msg.args[0]):
@@ -118,12 +221,9 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
channel)
if self.registryValue('randomGrabber', channel):
if len(payload) > length and len(payload.split()) > words:
- db = self.getDb(channel)
- cursor = db.cursor()
- cursor.execute("""SELECT added_at FROM quotegrabs
- WHERE nick=%s
- ORDER BY id DESC LIMIT 1""", msg.nick)
- if cursor.rowcount == 0:
+ try:
+ self.db.select(channel, msg.nick)
+ except dbi.NoRecordError:
self._grab(irc, msg, irc.prefix)
self._sendGrabMsg(irc, msg)
else:
@@ -134,21 +234,7 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
self._sendGrabMsg(irc, msg)
def _grab(self, irc, msg, addedBy):
- channel = msg.args[0]
- db = self.getDb(channel)
- cursor = db.cursor()
- text = ircmsgs.prettyPrint(msg)
- # Check to see if the latest quotegrab is identical
- cursor.execute("""SELECT quote FROM quotegrabs
- WHERE nick=%s
- ORDER BY id DESC LIMIT 1""", msg.nick)
- if cursor.rowcount != 0:
- if text == cursor.fetchone()[0]:
- return
- cursor.execute("""INSERT INTO quotegrabs
- VALUES (NULL, %s, %s, %s, %s, %s)""",
- msg.nick, msg.prefix, addedBy, int(time.time()), text)
- db.commit()
+ self.db.add(msg, addedBy)
def _sendGrabMsg(self, irc, msg):
s = 'jots down a new quote for %s' % msg.nick
@@ -161,13 +247,17 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
is only necessary if the message isn't sent in the channel
itself.
"""
- if ircutils.strEqual(nick, msg.nick):
- irc.error('You can\'t quote grab yourself.')
- return
+ # chan is used to make sure we know where to grab the quote from, as
+ # opposed to channel which is used to determine which db to store the
+ # quote in
+ chan = msg.args[0]
+ if chan is None:
+ raise callbacks.ArgumentError
+ if ircutils.nickEqual(nick, msg.nick):
+ irc.error('You can\'t quote grab yourself.', Raise=True)
for m in reversed(irc.state.history):
- # XXX Note that the channel isn't used here. Also note that
- # channel might be None since we use channeldb
- if m.command == 'PRIVMSG' and ircutils.strEqual(m.nick, nick):
+ if m.command == 'PRIVMSG' and ircutils.nickEqual(m.nick, nick) \
+ and ircutils.strEqual(m.args[0], chan):
self._grab(irc, m, msg.prefix)
irc.replySuccess()
return
@@ -180,16 +270,11 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
Returns 's latest quote grab in . is only
necessary if the message isn't sent in the channel itself.
"""
- db = self.getDb(channel)
- cursor = db.cursor()
- cursor.execute("""SELECT quote FROM quotegrabs
- WHERE nickeq(nick, %s)
- ORDER BY id DESC LIMIT 1""", nick)
- if cursor.rowcount == 0:
- irc.error('I couldn\'t find a matching quotegrab for %s.' % nick)
- else:
- text = cursor.fetchone()[0]
- irc.reply(text)
+ try:
+ irc.reply(self.db.getQuote(channel, nick))
+ except dbi.NoRecordError:
+ irc.error('I couldn\'t find a matching quotegrab for %s.' % nick,
+ Raise=True)
quote = wrap(quote, ['channeldb', 'nick'])
def list(self, irc, msg, args, channel, nick):
@@ -200,52 +285,36 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
full quote. is only necessary if the message isn't sent in
the channel itself.
"""
- # XXX This doesn't seem to be channel-specific in practice.
- db = self.getDb(channel)
- cursor = db.cursor()
- cursor.execute("""SELECT id, quote FROM quotegrabs
- WHERE nick LIKE %s
- ORDER BY id ASC""", nick)
- if cursor.rowcount == 0:
- irc.error('I couldn\'t find any quotegrabs for %s.' % nick)
- else:
- l = []
- for (id, quote) in cursor.fetchall():
+ try:
+ records = self.db.list(channel, nick)
+ L = []
+ for record in records:
# strip the nick from the quote
- quote = quote.replace('<%s> ' % nick, '', 1)
- item_str = utils.ellipsisify('#%s: %s' % (id, quote), 50)
- l.append(item_str)
- irc.reply(utils.commaAndify(l))
+ quote = record.text.replace('<%s> ' % nick, '', 1)
+ item = utils.ellipsisify('#%s: %s' % (record.id, quote), 50)
+ L.append(item)
+ irc.reply(utils.commaAndify(L))
+ except dbi.NoRecordError:
+ irc.error('I couldn\'t find any quotegrabs for %s.' % nick,
+ Raise=True)
list = wrap(list, ['channeldb', 'nick'])
- # XXX Could we rename this "random", to fit in more with the rest of
- # our plugins?
- def randomquote(self, irc, msg, args, channel, nick):
+ def random(self, irc, msg, args, channel, nick):
"""[] []
Returns a randomly grabbed quote, optionally choosing only from those
quotes grabbed for . is only necessary if the message
isn't sent in the channel itself.
"""
- db = self.getDb(channel)
- cursor = db.cursor()
- if nick:
- cursor.execute("""SELECT quote FROM quotegrabs
- WHERE nick LIKE %s ORDER BY random() LIMIT 1""",
- nick)
- else:
- cursor.execute("""SELECT quote FROM quotegrabs
- ORDER BY random() LIMIT 1""")
- if cursor.rowcount == 0:
+ try:
+ irc.reply(self.db.random(channel, nick))
+ except dbi.NoRecordError:
if nick:
irc.error('Couldn\'t get a random quote for that nick.')
else:
irc.error('Couldn\'t get a random quote. Are there any'
- 'grabbed quotes in the database?')
- return
- quote = cursor.fetchone()[0]
- irc.reply(quote)
- randomquote = wrap(randomquote, ['channeldb', additional('nick')])
+ 'grabbed quotes in the database?')
+ random = wrap(random, ['channeldb', additional('nick')])
def get(self, irc, msg, args, channel, id):
"""[]
@@ -253,22 +322,10 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
Return the quotegrab with the given . is only necessary
if the message isn't sent in the channel itself.
"""
- db = self.getDb(channel)
- cursor = db.cursor()
- cursor.execute("""SELECT quote, hostmask, added_at, added_by
- FROM quotegrabs WHERE id = %s""", id)
- if cursor.rowcount == 0:
- irc.error('No quotegrab for id %s' % utils.quoted(id))
- return
- quote, hostmask, timestamp, grabber_mask = cursor.fetchone()
- time_str = time.strftime(conf.supybot.humanTimestampFormat(),
- time.localtime(float(timestamp)))
try:
- grabber = ircdb.users.getUser(grabber_mask).name
- except:
- grabber = grabber_mask
- irc.reply('%s (Said by: %s; grabbed by %s at %s)' % \
- (quote, hostmask, grabber, time_str))
+ irc.reply(self.db.get(channel, id))
+ except dbi.NoRecordError:
+ irc.error('No quotegrab for id %s' % utils.quoted(id), Raise=True)
get = wrap(get, ['channeldb', 'id'])
diff --git a/plugins/__init__.py b/plugins/__init__.py
index 36a55c831..f09dbc69c 100644
--- a/plugins/__init__.py
+++ b/plugins/__init__.py
@@ -65,12 +65,11 @@ try:
for (name, module) in sys.modules.items():
if name.startswith('mx'):
mxCrap[name] = module
- sys.modules[name] = None
+ sys.modules.pop(name)
# Now that the mx crap is gone, we can import sqlite.
import sqlite
# And now we'll put it back, even though it sucks.
- for (name, module) in mxCrap.items():
- sys.modules[name] = module
+ sys.modules.update(mxCrap)
# Just in case, we'll do this as well. It doesn't seem to work fine by
# itself, though, or else we'd just do this in the first place.
sqlite.have_datetime = False
diff --git a/test/test_QuoteGrabs.py b/test/test_QuoteGrabs.py
index 1b41ce437..a636b981b 100644
--- a/test/test_QuoteGrabs.py
+++ b/test/test_QuoteGrabs.py
@@ -38,7 +38,7 @@ except ImportError:
if sqlite:
- class QuoteGrabsTestCase(ChannelPluginTestCase, PluginDocumentation):
+ class QuoteGrabsTestCase(ChannelPluginTestCase):
plugins = ('QuoteGrabs',)
def testQuoteGrab(self):
testPrefix = 'foo!bar@baz'
@@ -91,16 +91,16 @@ if sqlite:
self.assertNotError('quotegrabs list FOO')
self.assertNotError('quotegrabs list fOo')
- def testRandomquote(self):
+ def testRandom(self):
testPrefix = 'foo!bar@baz'
- self.assertError('randomquote')
+ self.assertError('random')
self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'test',
prefix=testPrefix))
- self.assertError('randomquote') # still none in the db
+ self.assertError('random') # still none in the db
self.assertNotError('grab foo')
- self.assertResponse('randomquote', ' test')
- self.assertResponse('randomquote foo', ' test')
- self.assertResponse('randomquote FOO', ' test')
+ self.assertResponse('random', ' test')
+ self.assertResponse('random foo', ' test')
+ self.assertResponse('random FOO', ' test')
def testGet(self):
testPrefix= 'foo!bar@baz'
@@ -111,5 +111,8 @@ if sqlite:
self.assertNotError('grab foo')
self.assertNotError('quotegrabs get 1')
+ class QuoteGrabsNonChannelTestCase(QuoteGrabsTestCase):
+ config = { 'databases.plugins.channelSpecific' : False }
+
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: