wrapified

This commit is contained in:
James Vega 2004-12-15 16:37:26 +00:00
parent caae2dd608
commit c8af5c886b
3 changed files with 99 additions and 92 deletions

View File

@ -43,11 +43,11 @@ import dictclient
import supybot.conf as conf import supybot.conf as conf
import supybot.utils as utils import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins import supybot.plugins as plugins
import supybot.webutils as webutils
import supybot.registry as registry
import supybot.ircutils as ircutils import supybot.ircutils as ircutils
import supybot.privmsgs as privmsgs import supybot.registry as registry
import supybot.webutils as webutils
import supybot.callbacks as callbacks import supybot.callbacks as callbacks
def configure(advanced): def configure(advanced):
@ -84,6 +84,7 @@ class Dict(callbacks.Privmsg):
irc.reply(utils.commaAndify(dbs)) irc.reply(utils.commaAndify(dbs))
except socket.error, e: except socket.error, e:
irc.error(webutils.strError(e)) irc.error(webutils.strError(e))
dictionaries = wrap(dictionaries)
def random(self, irc, msg, args): def random(self, irc, msg, args):
"""takes no arguments """takes no arguments
@ -97,23 +98,21 @@ class Dict(callbacks.Privmsg):
irc.reply(random.choice(dbs)) irc.reply(random.choice(dbs))
except socket.error, e: except socket.error, e:
irc.error(webutils.strError(e)) irc.error(webutils.strError(e))
random = wrap(random)
def dict(self, irc, msg, args): def dict(self, irc, msg, args, words):
"""[<dictionary>] <word> """[<dictionary>] <word>
Looks up the definition of <word> on dict.org's dictd server. Looks up the definition of <word> on dict.org's dictd server.
""" """
if not args:
raise callbacks.ArgumentError
try: try:
server = conf.supybot.plugins.Dict.server() server = conf.supybot.plugins.Dict.server()
conn = dictclient.Connection(server) conn = dictclient.Connection(server)
except socket.error, e: except socket.error, e:
irc.error(webutils.strError(e)) irc.error(webutils.strError(e), Raise=True)
return
dbs = sets.Set(conn.getdbdescs()) dbs = sets.Set(conn.getdbdescs())
if args[0] in dbs: if words[0] in dbs:
dictionary = args.pop(0) dictionary = words.pop(0)
else: else:
default = self.registryValue('default', msg.args[0]) default = self.registryValue('default', msg.args[0])
if default in dbs: if default in dbs:
@ -123,9 +122,9 @@ class Dict(callbacks.Privmsg):
self.log.info('Default dict for %s is not a supported ' self.log.info('Default dict for %s is not a supported '
'dictionary: %s.', msg.args[0], default) 'dictionary: %s.', msg.args[0], default)
dictionary = '*' dictionary = '*'
word = privmsgs.getArgs(args) if not words:
if not word:
irc.error('You must give a word to define.', Raise=True) irc.error('You must give a word to define.', Raise=True)
word = ' '.join(words)
definitions = conn.define(dictionary, word) definitions = conn.define(dictionary, word)
dbs = sets.Set() dbs = sets.Set()
if not definitions: if not definitions:
@ -149,6 +148,7 @@ class Dict(callbacks.Privmsg):
else: else:
s = '; '.join(L) s = '; '.join(L)
irc.reply(s) irc.reply(s)
dict = wrap(dict, [many('something')])
Class = Dict Class = Dict

View File

@ -54,6 +54,7 @@ import supybot.registry as registry
import supybot.conf as conf import supybot.conf as conf
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
import supybot.utils as utils import supybot.utils as utils
from supybot.commands import *
import supybot.ircmsgs as ircmsgs import supybot.ircmsgs as ircmsgs
import supybot.ircutils as ircutils import supybot.ircutils as ircutils
import supybot.privmsgs as privmsgs import supybot.privmsgs as privmsgs
@ -123,6 +124,13 @@ conf.registerChannelValue(conf.supybot.plugins.MoobotFactoids,
class SqliteMoobotDB(object): class SqliteMoobotDB(object):
def __init__(self, filename): def __init__(self, filename):
self.filename = filename self.filename = filename
self.dbs = ircutils.IrcDict()
def close(self):
for db in self.dbs.itervalues():
db.close()
self.dbs.clear()
def _getDb(self, channel): def _getDb(self, channel):
try: try:
import sqlite import sqlite
@ -130,26 +138,29 @@ class SqliteMoobotDB(object):
raise callbacks.Error, \ raise callbacks.Error, \
'You need to have PySQLite installed to use this ' \ 'You need to have PySQLite installed to use this ' \
'plugin. Download it at <http://pysqlite.sf.net/>' 'plugin. Download it at <http://pysqlite.sf.net/>'
if channel in self.dbs:
return self.dbs[channel]
filename = plugins.makeChannelFilename(self.filename, channel) filename = plugins.makeChannelFilename(self.filename, channel)
if os.path.exists(filename): if os.path.exists(filename):
db = sqlite.connect(filename) self.dbs[channel] = sqlite.connect(filename)
else: return self.dbs[channel]
db = sqlite.connect(filename) db = sqlite.connect(filename)
cursor = db.cursor() self.dbs[channel] = db
cursor.execute("""CREATE TABLE factoids ( cursor = db.cursor()
key TEXT PRIMARY KEY, cursor.execute("""CREATE TABLE factoids (
created_by INTEGER, key TEXT PRIMARY KEY,
created_at TIMESTAMP, created_by INTEGER,
modified_by INTEGER, created_at TIMESTAMP,
modified_at TIMESTAMP, modified_by INTEGER,
locked_at TIMESTAMP, modified_at TIMESTAMP,
locked_by INTEGER, locked_at TIMESTAMP,
last_requested_by TEXT, locked_by INTEGER,
last_requested_at TIMESTAMP, last_requested_by TEXT,
fact TEXT, last_requested_at TIMESTAMP,
requested_count INTEGER fact TEXT,
)""") requested_count INTEGER
db.commit() )""")
db.commit()
return db return db
def getFactoid(self, channel, key): def getFactoid(self, channel, key):
@ -288,6 +299,7 @@ class SqliteMoobotDB(object):
def getKeysByGlob(self, channel, glob): def getKeysByGlob(self, channel, glob):
db = self._getDb(channel) db = self._getDb(channel)
cursor = db.cursor() cursor = db.cursor()
glob = '%%%s%%' % glob
cursor.execute("""SELECT key FROM factoids WHERE key LIKE %s cursor.execute("""SELECT key FROM factoids WHERE key LIKE %s
ORDER BY key""", glob) ORDER BY key""", glob)
if cursor.rowcount == 0: if cursor.rowcount == 0:
@ -298,6 +310,7 @@ class SqliteMoobotDB(object):
def getKeysByValueGlob(self, channel, glob): def getKeysByValueGlob(self, channel, glob):
db = self._getDb(channel) db = self._getDb(channel)
cursor = db.cursor() cursor = db.cursor()
glob = '%%%s%%' % glob
cursor.execute("""SELECT key FROM factoids WHERE fact LIKE %s cursor.execute("""SELECT key FROM factoids WHERE fact LIKE %s
ORDER BY key""", glob) ORDER BY key""", glob)
if cursor.rowcount == 0: if cursor.rowcount == 0:
@ -307,21 +320,28 @@ class SqliteMoobotDB(object):
MoobotDB = plugins.DB('MoobotFactoids', {'sqlite': SqliteMoobotDB}) MoobotDB = plugins.DB('MoobotFactoids', {'sqlite': SqliteMoobotDB})
## We define our own getChannel so we can set raiseError=False in one place. ## We define our own getChannel so we can take advantage of the channeldb
# Actually, we'll go ahead and raise the error and say that everything must be # wrapper from non-command methods. This means that the bot will use
# in channel. The smart users will know that they can specify the channel name # supybot.databases.plugins.channelSpecific.channel as the default channel
# for certain commands. # when the user is talking to the bot privately.
def getChannel(irc, msg, args=()): def getChannel(irc, msg, args=()):
try: spec = Spec(['channeldb'])
return privmsgs.getChannel(msg, args) state = spec(irc, msg, args)
except callbacks.Error: return state.channel
irc.error('Command must be sent in a channel.', Raise=True)
class MoobotFactoids(callbacks.Privmsg): class MoobotFactoids(callbacks.Privmsg):
callBefore = ['Dunno'] callBefore = ['Dunno']
def __init__(self): def __init__(self):
self.db = MoobotDB() self.db = MoobotDB()
super(MoobotFactoids, self).__init__() self.__parent = super(MoobotFactoids, self)
self.__parent.__init__()
def die(self):
self.__parent.die()
self.db.close()
def reset(self):
self.db.close()
_replyTag = '<reply>' _replyTag = '<reply>'
_actionTag = '<action>' _actionTag = '<action>'
@ -462,28 +482,25 @@ class MoobotFactoids(callbacks.Privmsg):
self.db.addFactoid(channel, key, fact, id) self.db.addFactoid(channel, key, fact, id)
irc.replySuccess() irc.replySuccess()
def literal(self, irc, msg, args): def literal(self, irc, msg, args, channel, key):
"""[<channel>] <factoid key> """[<channel>] <factoid key>
Returns the literal factoid for the given factoid key. No parsing of Returns the literal factoid for the given factoid key. No parsing of
the factoid value is done as it is with normal retrieval. <channel> the factoid value is done as it is with normal retrieval. <channel>
is only necessary if the message isn't sent in the channel itself. is only necessary if the message isn't sent in the channel itself.
""" """
channel = getChannel(irc, msg, args)
key = privmsgs.getArgs(args)
fact = self._getFactoid(irc, channel, key) fact = self._getFactoid(irc, channel, key)
fact = fact[0] fact = fact[0]
irc.reply(fact) irc.reply(fact)
literal = wrap(literal, ['channeldb', 'text'])
def factinfo(self, irc, msg, args): def factinfo(self, irc, msg, args, channel, key):
"""[<channel>] <factoid key> """[<channel>] <factoid key>
Returns the various bits of info on the factoid for the given key. Returns the various bits of info on the factoid for the given key.
<channel> is only necessary if the message isn't sent in the channel <channel> is only necessary if the message isn't sent in the channel
itself. itself.
""" """
channel = getChannel(irc, msg, args)
key = privmsgs.getArgs(args)
# Start building the response string # Start building the response string
s = key + ": " s = key + ": "
# Next, get all the info and build the response piece by piece # Next, get all the info and build the response piece by piece
@ -521,17 +538,12 @@ class MoobotFactoids(callbacks.Privmsg):
lock_by = ircdb.users.getUser(locked_by).name lock_by = ircdb.users.getUser(locked_by).name
s += " Locked by %s on %s." % (lock_by, lock_at) s += " Locked by %s on %s." % (lock_by, lock_at)
irc.reply(s) irc.reply(s)
factinfo = wrap(factinfo, ['channeldb', 'text'])
def _lock(self, irc, msg, args, locking=True): def _lock(self, irc, msg, channel, user, key, locking=True):
self.log.debug('in _lock') #self.log.debug('in _lock')
try: #self.log.debug('id: %s' % id)
id = ircdb.users.getUserId(msg.prefix) id = user.id
except KeyError:
irc.errorNotRegistered()
return
self.log.debug('id: %s' % id)
channel = getChannel(irc, msg, args)
key = privmsgs.getArgs(args)
info = self.db.getFactinfo(channel, key) info = self.db.getFactinfo(channel, key)
if not info: if not info:
irc.error('No such factoid: "%s"' % key) irc.error('No such factoid: "%s"' % key)
@ -545,8 +557,8 @@ class MoobotFactoids(callbacks.Privmsg):
irc.error('Factoid "%s" is not locked.' % key) irc.error('Factoid "%s" is not locked.' % key)
return return
# Can only lock/unlock own factoids unless you're an admin # Can only lock/unlock own factoids unless you're an admin
self.log.debug('admin?: %s' % ircdb.checkCapability(id, 'admin')) #self.log.debug('admin?: %s' % ircdb.checkCapability(id, 'admin'))
self.log.debug('created_by: %s' % created_by) #self.log.debug('created_by: %s' % created_by)
if not (ircdb.checkCapability(id, 'admin') or created_by == id): if not (ircdb.checkCapability(id, 'admin') or created_by == id):
if locking: if locking:
s = "lock" s = "lock"
@ -562,25 +574,27 @@ class MoobotFactoids(callbacks.Privmsg):
self.db.unlock(channel, key) self.db.unlock(channel, key)
irc.replySuccess() irc.replySuccess()
def lock(self, irc, msg, args): def lock(self, irc, msg, args, channel, user, key):
"""[<channel>] <factoid key> """[<channel>] <factoid key>
Locks the factoid with the given factoid key. Requires that the user Locks the factoid with the given factoid key. Requires that the user
be registered and have created the factoid originally. <channel> is be registered and have created the factoid originally. <channel> is
only necessary if the message isn't sent in the channel itself. only necessary if the message isn't sent in the channel itself.
""" """
self._lock(irc, msg, args, True) self._lock(irc, msg, channel, user, key, True)
lock = wrap(lock, ['channeldb', 'user', 'text'])
def unlock(self, irc, msg, args): def unlock(self, irc, msg, args, channel, user, key):
"""[<channel>] <factoid key> """[<channel>] <factoid key>
Unlocks the factoid with the given factoid key. Requires that the Unlocks the factoid with the given factoid key. Requires that the
user be registered and have locked the factoid. <channel> is only user be registered and have locked the factoid. <channel> is only
necessary if the message isn't sent in the channel itself. necessary if the message isn't sent in the channel itself.
""" """
self._lock(irc, msg, args, False) self._lock(irc, msg, channel, user, key, False)
unlock = wrap(unlock, ['channeldb', 'user', 'text'])
def most(self, irc, msg, args): def most(self, irc, msg, args, channel, method):
"""[<channel>] {popular|authored|recent} """[<channel>] {popular|authored|recent}
Lists the most {popular|authored|recent} factoids. "popular" lists the Lists the most {popular|authored|recent} factoids. "popular" lists the
@ -589,14 +603,14 @@ class MoobotFactoids(callbacks.Privmsg):
<channel> is only necessary if the message isn't sent in the channel <channel> is only necessary if the message isn't sent in the channel
itself. itself.
""" """
channel = getChannel(irc, msg, args) method = method.capitalize()
arg = privmsgs.getArgs(args) method = getattr(self, '_most%s' % method, None)
arg = arg.capitalize()
method = getattr(self, '_most%s' % arg, None)
if method is None: if method is None:
raise callbacks.ArgumentError raise callbacks.ArgumentError
limit = self.registryValue('mostCount', channel) limit = self.registryValue('mostCount', channel)
method(irc, channel, limit) method(irc, channel, limit)
most = wrap(most, ['channeldb',
('literal', ('popular', 'authored', 'recent'))])
def _mostAuthored(self, irc, channel, limit): def _mostAuthored(self, irc, channel, limit):
results = self.db.mostAuthored(channel, limit) results = self.db.mostAuthored(channel, limit)
@ -628,7 +642,7 @@ class MoobotFactoids(callbacks.Privmsg):
else: else:
irc.error('No factoids have been requested from my database.') irc.error('No factoids have been requested from my database.')
def listauth(self, irc, msg, args): def listauth(self, irc, msg, args, channel, author):
"""[<channel>] <author name> """[<channel>] <author name>
Lists the keys of the factoids with the given author. Note that if an Lists the keys of the factoids with the given author. Note that if an
@ -636,8 +650,6 @@ class MoobotFactoids(callbacks.Privmsg):
this function (so don't use integer usernames!). <channel> is only this function (so don't use integer usernames!). <channel> is only
necessary if the message isn't sent in the channel itself. necessary if the message isn't sent in the channel itself.
""" """
channel = getChannel(irc, msg, args)
author = privmsgs.getArgs(args)
try: try:
id = ircdb.users.getUserId(author) id = ircdb.users.getUserId(author)
except KeyError: except KeyError:
@ -650,18 +662,16 @@ class MoobotFactoids(callbacks.Privmsg):
s = 'Author search for "%s" (%s found): %s' % \ s = 'Author search for "%s" (%s found): %s' % \
(author, len(keys), utils.commaAndify(keys)) (author, len(keys), utils.commaAndify(keys))
irc.reply(s) irc.reply(s)
listauth = wrap(listauth, ['channeldb', 'something'])
def listkeys(self, irc, msg, args): def listkeys(self, irc, msg, args, channel, search):
"""[<channel>] <text> """[<channel>] <text>
Lists the keys of the factoids whose key contains the provided text. Lists the keys of the factoids whose key contains the provided text.
<channel> is only necessary if the message isn't sent in the channel <channel> is only necessary if the message isn't sent in the channel
itself. itself.
""" """
channel = getChannel(irc, msg, args) results = self.db.getKeysByGlob(channel, search)
search = privmsgs.getArgs(args)
glob = '%' + search + '%'
results = self.db.getKeysByGlob(channel, glob)
if not results: if not results:
irc.reply('No keys matching "%s" found.' % search) irc.reply('No keys matching "%s" found.' % search)
elif len(results) == 1 and \ elif len(results) == 1 and \
@ -673,18 +683,16 @@ class MoobotFactoids(callbacks.Privmsg):
s = 'Key search for "%s" (%s found): %s' % \ s = 'Key search for "%s" (%s found): %s' % \
(search, len(keys), utils.commaAndify(keys)) (search, len(keys), utils.commaAndify(keys))
irc.reply(s) irc.reply(s)
listkeys = wrap(listkeys, ['channeldb', 'text'])
def listvalues(self, irc, msg, args): def listvalues(self, irc, msg, args, channel, search):
"""[<channel>] <text> """[<channel>] <text>
Lists the keys of the factoids whose value contains the provided text. Lists the keys of the factoids whose value contains the provided text.
<channel> is only necessary if the message isn't sent in the channel <channel> is only necessary if the message isn't sent in the channel
itself. itself.
""" """
channel = getChannel(irc, msg, args) results = self.db.getKeysByValueGlob(channel, search)
search = privmsgs.getArgs(args)
glob = '%' + search + '%'
results = self.db.getKeysByValueGlob(channel, glob)
if not results: if not results:
irc.reply('No values matching "%s" found.' % search) irc.reply('No values matching "%s" found.' % search)
return return
@ -692,36 +700,35 @@ class MoobotFactoids(callbacks.Privmsg):
s = 'Value search for "%s" (%s found): %s' % \ s = 'Value search for "%s" (%s found): %s' % \
(search, len(keys), utils.commaAndify(keys)) (search, len(keys), utils.commaAndify(keys))
irc.reply(s) irc.reply(s)
listvalues = wrap(listvalues, ['channeldb', 'text'])
def delete(self, irc, msg, args): def remove(self, irc, msg, args, channel, key):
"""[<channel>] <factoid key> """[<channel>] <factoid key>
Deletes the factoid with the given key. <channel> is only necessary Deletes the factoid with the given key. <channel> is only necessary
if the message isn't sent in the channel itself. if the message isn't sent in the channel itself.
""" """
channel = getChannel(irc, msg, args)
key = privmsgs.getArgs(args)
_ = self._getUserId(irc, msg.prefix) _ = self._getUserId(irc, msg.prefix)
_ = self._getFactoid(irc, channel, key) _ = self._getFactoid(irc, channel, key)
self._checkNotLocked(irc, channel, key) self._checkNotLocked(irc, channel, key)
self.db.removeFactoid(channel, key) self.db.removeFactoid(channel, key)
irc.replySuccess() irc.replySuccess()
remove = wrap(remove, ['channeldb', 'text'])
# XXX What the heck? Why are there two definitions of randomfactoid? def random(self, irc, msg, args, channel):
def randomfactoid(self, irc, msg, args):
"""[<channel>] """[<channel>]
Displays a random factoid (along with its key) from the database. Displays a random factoid (along with its key) from the database.
<channel> is only necessary if the message isn't sent in the channel <channel> is only necessary if the message isn't sent in the channel
itself. itself.
""" """
channel = getChannel(irc, msg, args)
results = self.db.randomFactoid(channel) results = self.db.randomFactoid(channel)
if not results: if not results:
irc.error('No factoids in the database.') irc.error('No factoids in the database.')
return return
(fact, key) = results (fact, key) = results
irc.reply('Random factoid: "%s" is "%s"' % (key, fact)) irc.reply('Random factoid: "%s" is "%s"' % (key, fact))
random = wrap(random, ['channeldb'])
Class = MoobotFactoids Class = MoobotFactoids

View File

@ -65,7 +65,7 @@ if sqlite is not None:
self._testOptions('(|a)', ['a', '']) self._testOptions('(|a)', ['a', ''])
self._testOptions('((a)|(b))', ['(a)', '(b)']) self._testOptions('((a)|(b))', ['(a)', '(b)'])
class FactoidsTestCase(ChannelPluginTestCase, PluginDocumentation): class FactoidsTestCase(ChannelPluginTestCase):
plugins = ('MoobotFactoids', 'User', 'Utilities') plugins = ('MoobotFactoids', 'User', 'Utilities')
config = {'reply.whenNotCommand': False} config = {'reply.whenNotCommand': False}
def setUp(self): def setUp(self):
@ -116,7 +116,7 @@ if sqlite is not None:
self.assertResponse('mOo', 'foo') self.assertResponse('mOo', 'foo')
self.assertResponse('MoO', 'foo') self.assertResponse('MoO', 'foo')
# Check the "_is_" ability # Check the "_is_" ability
self.assertNotError('delete moo') self.assertNotError('remove moo')
self.assertNotError('moo _is_ <reply>foo') self.assertNotError('moo _is_ <reply>foo')
self.assertResponse('moo', 'foo') self.assertResponse('moo', 'foo')
self.assertNotError('foo is bar _is_ baz') self.assertNotError('foo is bar _is_ baz')
@ -279,12 +279,12 @@ if sqlite is not None:
self.assertRegexp('listauth tester', 'tester.*\(1 found\):.*moo') self.assertRegexp('listauth tester', 'tester.*\(1 found\):.*moo')
self.assertError('listauth moo') self.assertError('listauth moo')
def testDelete(self): def testRemove(self):
self.assertNotError('moo is <reply>moo') self.assertNotError('moo is <reply>moo')
self.assertNotError('lock moo') self.assertNotError('lock moo')
self.assertError('delete moo') self.assertError('remove moo')
self.assertNotError('unlock moo') self.assertNotError('unlock moo')
self.assertNotError('delete moo') self.assertNotError('remove moo')
def testAugmentFactoid(self): def testAugmentFactoid(self):
self.assertNotError('moo is foo') self.assertNotError('moo is foo')
@ -332,10 +332,10 @@ if sqlite is not None:
self.assertResponse('listkeys foo', 'Key search for "foo" ' self.assertResponse('listkeys foo', 'Key search for "foo" '
'(1 found): "foo"') '(1 found): "foo"')
def testRandomFactoid(self): def testRandom(self):
self.assertNotError('foo is <reply>bar') self.assertNotError('foo is <reply>bar')
self.assertNotError('bar is <reply>baz') self.assertNotError('bar is <reply>baz')
self.assertRegexp('randomfactoid', r'bar|baz') self.assertRegexp('random', r'bar|baz')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: