Update QuoteGrabs to use db abstraction. Hopefully fix the mxCrap thing

for good.  This update should also fix the problem that had existed with
QuoteGrabs and databases.plugins.channelSpecifc=False
This commit is contained in:
James Vega 2004-12-03 15:41:08 +00:00
parent ac04024f47
commit 4ba005ddf6
3 changed files with 180 additions and 121 deletions

View File

@ -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 <http://pysqlite.sf.net/>'
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 ' \
'<http://pysqlite.sf.net/>'
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):
<channel> 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 <nick>'s latest quote grab in <channel>. <channel> 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. <channel> 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):
"""[<channel>] [<nick>]
Returns a randomly grabbed quote, optionally choosing only from those
quotes grabbed for <nick>. <channel> 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):
"""[<channel>] <id>
@ -253,22 +322,10 @@ class QuoteGrabs(plugins.ChannelDBHandler, callbacks.Privmsg):
Return the quotegrab with the given <id>. <channel> 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'])

View File

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

View File

@ -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', '<foo> test')
self.assertResponse('randomquote foo', '<foo> test')
self.assertResponse('randomquote FOO', '<foo> test')
self.assertResponse('random', '<foo> test')
self.assertResponse('random foo', '<foo> test')
self.assertResponse('random FOO', '<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: