Added Lart and Praise plugins, deprecated FunDB, converted Dunno and Success to the new plugins.ChannelIdDatabasePlugin.

This commit is contained in:
Jeremy Fincher 2004-10-28 17:20:37 +00:00
parent 3b5cde224d
commit 3899f33d54
8 changed files with 484 additions and 268 deletions

View File

@ -28,9 +28,9 @@
### ###
""" """
The Dunno module is used to spice up the 'replyWhenNotCommand' behavior with The Dunno module is used to spice up the reply when given an invalid command
random 'I dunno'-like responses. If you want something spicier than '<x> is with random 'I dunno'-like responses. If you want something spicier than
not a valid command'-like responses, use this plugin. '<x> is not a valid command'-like responses, use this plugin.
""" """
import supybot import supybot
@ -64,46 +64,13 @@ conf.registerChannelValue(conf.supybot.plugins.Dunno, 'prefixNick',
registry.Boolean(True, """Determines whether the bot will prefix the nick registry.Boolean(True, """Determines whether the bot will prefix the nick
of the user giving an invalid command to the "dunno" response.""")) of the user giving an invalid command to the "dunno" response."""))
class DbiDunnoDB(plugins.DbiChannelDB): class Dunno(plugins.ChannelIdDatabasePlugin):
class DB(dbi.DB):
class Record(dbi.Record):
__fields__ = [
'at',
'by',
'text',
]
def __init__(self, filename):
# We use self.__class__ here because apparently DB isn't in our
# scope. python--
self.__parent = super(self.__class__, self)
self.__parent.__init__(filename)
def add(self, text, by, at):
return self.__parent.add(self.Record(at=at, by=by, text=text))
def change(self, id, f):
dunno = self.get(id)
dunno.text = f(dunno.text)
self.set(id, dunno)
DunnoDB = plugins.DB('Dunno', {'flat': DbiDunnoDB})
class Dunno(callbacks.Privmsg):
"""This plugin was written initially to work with MoobotFactoids, the two """This plugin was written initially to work with MoobotFactoids, the two
of them to provide a similar-to-moobot-and-blootbot interface for factoids. of them to provide a similar-to-moobot-and-blootbot interface for factoids.
Basically, it replaces the standard 'Error: <X> is not a valid command.' Basically, it replaces the standard 'Error: <X> is not a valid command.'
messages with messages kept in a database, able to give more personable messages with messages kept in a database, able to give more personable
responses.""" responses."""
callAfter = ['MoobotFactoids'] callAfter = ['MoobotFactoids']
def __init__(self):
self.__parent = super(Dunno, self)
self.__parent.__init__()
self.db = DunnoDB()
def die(self):
self.db.close()
self.__parent.die()
def invalidCommand(self, irc, msg, tokens): def invalidCommand(self, irc, msg, tokens):
channel = msg.args[0] channel = msg.args[0]
if ircutils.isChannel(channel): if ircutils.isChannel(channel):
@ -114,107 +81,6 @@ class Dunno(callbacks.Privmsg):
dunno = ircutils.standardSubstitute(irc, msg, dunno) dunno = ircutils.standardSubstitute(irc, msg, dunno)
irc.reply(dunno, prefixName=prefixName) irc.reply(dunno, prefixName=prefixName)
def add(self, irc, msg, args, user, at, channel, dunno):
"""[<channel>] <text>
Adds <text> as a "dunno" to be used as a random response when no
command or factoid key matches. Can optionally contain '$who', which
will be replaced by the user's name when the dunno is displayed.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
id = self.db.add(channel, dunno, user.id, at)
irc.replySuccess('Dunno #%s added.' % id)
add = wrap(add, ['user', 'now', 'channeldb', 'text'])
def remove(self, irc, msg, args, user, channel, id):
"""[<channel>] <id>
Removes dunno with the given <id>. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
# Must be registered to use this
try:
dunno = self.db.get(channel, id)
if user.id != dunno.by:
# XXX We need to come up with a way to handle this capability
# checking when channel is None. It'll probably involve
# something along the lines of using admin instead of
# #channel,op. The function should be added to
# plugins/__init__.py
cap = ircdb.makeChannelCapability(channel, 'op')
if not ircdb.users.checkCapability(cap):
irc.errorNoCapability(cap)
self.db.remove(channel, id)
irc.replySuccess()
except KeyError:
irc.error('No dunno has id #%s.' % id)
remove = wrap(remove, ['user', 'channeldb', ('id', 'dunno')])
def search(self, irc, msg, args, channel, text):
"""[<channel>] <text>
Search for dunno containing the given text. Returns the ids of the
dunnos with the text in them. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
def p(dunno):
return text.lower() in dunno.text.lower()
ids = [str(dunno.id) for dunno in self.db.select(channel, p)]
if ids:
s = 'Dunno search for %s (%s found): %s.' % \
(utils.quoted(text), len(ids), utils.commaAndify(ids))
irc.reply(s)
else:
irc.reply('No dunnos found matching that search criteria.')
search = wrap(search, ['channeldb', 'text'])
def get(self, irc, msg, args, channel, id):
"""[<channel>] <id>
Display the text of the dunno with the given id. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
try:
dunno = self.db.get(channel, id)
name = ircdb.users.getUser(dunno.by).name
at = time.localtime(dunno.at)
timeStr = time.strftime(conf.supybot.humanTimestampFormat(), at)
irc.reply("Dunno #%s: %s (added by %s at %s)" % \
(id, utils.quoted(dunno.text), name, timeStr))
except KeyError:
irc.error('No dunno found with that id.')
get = wrap(get, ['channeldb', ('id', 'dunno')])
def change(self, irc, msg, args, channel, id, replacer):
"""[<channel>] <id> <regexp>
Alters the dunno with the given id according to the provided regexp.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
try:
# Should this check that Record.by == user.id ||
# checkChannelCapability like remove() does?
self.db.change(channel, id, replacer)
except KeyError:
irc.error('There is no dunno #%s.' % id)
return
irc.replySuccess()
change = wrap(change, ['channeldb', ('id', 'dunno'), 'regexpReplacer'])
def stats(self, irc, msg, args, channel):
"""[<channel>]
Returns the number of dunnos in the dunno database. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
num = self.db.size(channel)
irc.reply('There %s %s in my database.' %
(utils.be(num), utils.nItems('dunno', num)))
stats = wrap(stats, ['channeldb'])
Class = Dunno Class = Dunno

View File

@ -31,6 +31,8 @@
Provides fun commands that require a database to operate. Provides fun commands that require a database to operate.
""" """
deprecated = True
__revision__ = "$Id$" __revision__ = "$Id$"
import supybot.plugins as plugins import supybot.plugins as plugins

118
plugins/Lart.py Normal file
View File

@ -0,0 +1,118 @@
###
# Copyright (c) 2004, Jeremiah Fincher
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
"""
This plugin keeps a database of larts, and larts with it.
"""
import supybot
__revision__ = "$Id$"
__author__ = supybot.authors.jemfinch
__contributors__ = {}
import re
import supybot.conf as conf
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.privmsgs as privmsgs
import supybot.registry as registry
import supybot.callbacks as callbacks
def configure(advanced):
# This will be called by setup.py to configure this module. Advanced is
# a bool that specifies whether the user identified himself as an advanced
# user or not. You should effect your configuration by manipulating the
# registry as appropriate.
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Lart', True)
Lart = conf.registerPlugin('Lart')
conf.registerChannelValue(Lart, 'showIds',
registry.Boolean(False, """Determines whether the bot will show the ids of
a lart when the lart is given."""))
class Lart(plugins.ChannelIdDatabasePlugin):
_meRe = re.compile(r'\bme\b', re.I)
_myRe = re.compile(r'\bmy\b', re.I)
def _replaceFirstPerson(self, s, nick):
s = self._meRe.sub(nick, s)
s = self._myRe.sub('%s\'s' % nick, s)
return s
def addValidator(self, irc, text):
if '$who' not in text:
irc.error('Larts must contain $who.', Raise=True)
def lart(self, irc, msg, args, channel, id, text):
"""[<channel>] [<id>] <who|what> [for <reason>]
Uses the Luser Attitude Readjustment Tool on <who|what> (for <reason>,
if given). If <id> is given, uses that specific lart. <channel> is
only necessary if the message isn't sent in the channel itself.
"""
if ' for ' in text:
(target, reason) = map(str.strip, text.split(' for ', 1))
else:
(target, reason) = (text, '')
if ircutils.strEqual(target, irc.nick):
target = msg.nick
reason = 'trying to dis me'
if id is not None:
try:
lart = self.db.get(channel, id)
except KeyError:
irc.error('There is no lart with id #%s.' % id)
return
else:
lart = self.db.random(channel)
if not lart:
irc.error('There are no larts in my database for %s.' %channel)
return
text = self._replaceFirstPerson(lart.text, msg.nick)
reason = self._replaceFirstPerson(reason, msg.nick)
if target.endswith('.'):
target = target.rstrip('.')
target = self._replaceFirstPerson(target, msg.nick)
text = text.replace('$who', target)
if reason:
text += ' for ' + reason
if self.registryValue('showIds', channel):
text += ' (#%s)' % lart.id
irc.reply(text, action=True)
lart = wrap(lart, ['channeldb', optional('id'), 'text'])
Class = Lart
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

117
plugins/Praise.py Normal file
View File

@ -0,0 +1,117 @@
###
# Copyright (c) 2004, Jeremiah Fincher
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
"""
Add the module docstring here. This will be used by the setup.py script.
"""
import supybot
__revision__ = "$Id$"
__author__ = supybot.authors.unknown
__contributors__ = {}
import re
import supybot.conf as conf
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.privmsgs as privmsgs
import supybot.registry as registry
import supybot.callbacks as callbacks
def configure(advanced):
# This will be called by setup.py to configure this module. Advanced is
# a bool that specifies whether the user identified himself as an advanced
# user or not. You should effect your configuration by manipulating the
# registry as appropriate.
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Praise', True)
Praise = conf.registerPlugin('Praise')
conf.registerChannelValue(Praise, 'showIds',
registry.Boolean(False, """Determines whether the bot will show the ids of
a praise when the praise is given."""))
class Praise(plugins.ChannelIdDatabasePlugin):
_meRe = re.compile(r'\bme\b', re.I)
_myRe = re.compile(r'\bmy\b', re.I)
def _replaceFirstPerson(self, s, nick):
s = self._meRe.sub(nick, s)
s = self._myRe.sub('%s\'s' % nick, s)
return s
def addValidator(self, irc, text):
if '$who' not in text:
irc.error('Praises must contain $who.', Raise=True)
def praise(self, irc, msg, args, channel, id, text):
"""[<channel>] [<id>] <who|what> [for <reason>]
Praises <who|what> (for <reason>, if given). If <id> is given, uses
that specific praise. <channel> is only necessary if the message isn't
sent in the channel itself.
"""
if ' for ' in text:
(target, reason) = map(str.strip, text.split(' for ', 1))
else:
(target, reason) = (text, '')
if ircutils.strEqual(target, irc.nick):
target = 'itself'
if id is not None:
try:
praise = self.db.get(channel, id)
except KeyError:
irc.error('There is no praise with id #%s.' % id)
return
else:
praise = self.db.random(channel)
if not praise:
irc.error('There are no praise in my database for %s.' %channel)
return
text = self._replaceFirstPerson(praise.text, msg.nick)
reason = self._replaceFirstPerson(reason, msg.nick)
if target.endswith('.'):
target = target.rstrip('.')
target = self._replaceFirstPerson(target, msg.nick)
text = text.replace('$who', target)
if reason:
text += ' for ' + reason
if self.registryValue('showIds', channel):
text += ' (#%s)' % praise.id
irc.reply(text, action=True)
praise = wrap(praise, ['channeldb', optional('id'), 'text'])
Class = Praise
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

68
plugins/Quote.py Normal file
View File

@ -0,0 +1,68 @@
###
# Copyright (c) 2002-2004, Jeremiah Fincher
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
"""
Maintains a Quotes database for each channel.
"""
__revision__ = "$Id$"
import re
import time
import getopt
import os.path
import supybot.dbi as dbi
import supybot.conf as conf
import supybot.utils as utils
import supybot.ircdb as ircdb
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.privmsgs as privmsgs
import supybot.registry as registry
import supybot.callbacks as callbacks
class Quotes(plugins.ChannelIdDatabasePlugin):
def random(self, irc, msg, args, channel):
"""[<channel>]
Returns a random quote from <channel>. <channel> is only necessary if
the message isn't sent in the channel itself.
"""
quote = self.db.random(channel)
if quote:
irc.reply(self.showRecord(quote))
else:
irc.error('I have no quotes in my database for %s.' % channel)
random = wrap(random, ['channeldb'])
Class = Quotes
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:

View File

@ -31,6 +31,8 @@
Maintains a Quotes database for each channel. Maintains a Quotes database for each channel.
""" """
deprecated = True
__revision__ = "$Id$" __revision__ = "$Id$"
import re import re

View File

@ -54,31 +54,7 @@ conf.registerChannelValue(conf.supybot.plugins.Success, 'prefixNick',
registry.Boolean(True, """Determines whether the bot will prefix the nick registry.Boolean(True, """Determines whether the bot will prefix the nick
of the user giving an invalid command to the success response.""")) of the user giving an invalid command to the success response."""))
class DbiSuccessDB(plugins.DbiChannelDB): class Success(plugins.ChannelIdDatabasePlugin):
class DB(dbi.DB):
class Record(dbi.Record):
__fields__ = [
'at',
'by',
'text',
]
def __init__(self, filename):
# We use self.__class__ here because apparently DB isn't in our
# scope. python--
self.__parent = super(self.__class__, self)
self.__parent.__init__(filename)
def add(self, text, by, at):
return self.__parent.add(self.Record(at=at, by=by, text=text))
def change(self, id, f):
dunno = self.get(id)
dunno.text = f(dunno.text)
self.set(id, dunno)
SuccessDB = plugins.DB('Success', {'flat': DbiSuccessDB})
class Success(callbacks.Privmsg):
"""This plugin was written initially to work with MoobotFactoids, the two """This plugin was written initially to work with MoobotFactoids, the two
of them to provide a similar-to-moobot-and-blootbot interface for factoids. of them to provide a similar-to-moobot-and-blootbot interface for factoids.
Basically, it replaces the standard 'Error: <X> is not a valid command.' Basically, it replaces the standard 'Error: <X> is not a valid command.'
@ -88,7 +64,6 @@ class Success(callbacks.Privmsg):
self.__parent = super(Success, self) self.__parent = super(Success, self)
self.__parent.__init__() self.__parent.__init__()
self.target = None self.target = None
self.db = SuccessDB()
pluginSelf = self pluginSelf = self
self.originalClass = conf.supybot.replies.success.__class__ self.originalClass = conf.supybot.replies.success.__class__
class MySuccessClass(self.originalClass): class MySuccessClass(self.originalClass):
@ -111,7 +86,6 @@ class Success(callbacks.Privmsg):
conf.supybot.replies.success.__class__ = MySuccessClass conf.supybot.replies.success.__class__ = MySuccessClass
def die(self): def die(self):
self.db.close()
self.__parent.die() self.__parent.die()
conf.supybot.replies.success.__class__ = self.originalClass conf.supybot.replies.success.__class__ = self.originalClass
@ -121,101 +95,6 @@ class Success(callbacks.Privmsg):
self.target = msg.args[0] self.target = msg.args[0]
return msg return msg
def add(self, irc, msg, args, user, at, channel, text):
"""[<channel>] <text>
Adds <text> as a "success" to be used as a random response when a
success message is needed. Can optionally contain '$who', which
will be replaced by the user's name when the dunno is displayed.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
id = self.db.add(channel, text, user.id, at)
irc.replySuccess('Success #%s added.' % id)
add = wrap(add, ['user', 'now', 'channeldb', 'text'])
def remove(self, irc, msg, args, channel, id, user):
"""[<channel>] <id>
Removes success with the given <id>. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
# Must be registered to use this
try:
success = self.db.get(channel, id)
if user.id != success.by:
cap = ircdb.makeChannelCapability(channel, 'op')
if not ircdb.users.checkCapability(cap):
irc.errorNoCapability(cap)
self.db.remove(channel, id)
irc.replySuccess()
except KeyError:
irc.error('No success has id #%s.' % id)
remove = wrap(remove, ['channeldb', ('id', 'success'), 'user'])
def search(self, irc, msg, args, channel, text):
"""[<channel>] <text>
Search for success containing the given text. Returns the ids of the
successes with the text in them. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
def p(success):
return text.lower() in success.text.lower()
ids = [str(success.id) for success in self.db.select(channel, p)]
if ids:
s = 'Success search for %s (%s found): %s.' % \
(utils.quoted(text), len(ids), utils.commaAndify(ids))
irc.reply(s)
else:
irc.reply('No successes found matching that search criteria.')
search = wrap(search, ['channeldb', 'text'])
def get(self, irc, msg, args, channel, id):
"""[<channel>] <id>
Display the text of the success with the given id. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
try:
success = self.db.get(channel, id)
name = ircdb.users.getUser(success.by).name
at = time.localtime(success.at)
timeStr = time.strftime(conf.supybot.humanTimestampFormat(), at)
irc.reply("Success #%s: %s (added by %s at %s)" % \
(id, utils.quoted(success.text), name, timeStr))
except KeyError:
irc.error('No success found with that id.')
get = wrap(get, ['channeldb', ('id', 'success')])
def change(self, irc, msg, args, user, channel, id, replacer):
"""[<channel>] <id> <regexp>
Alters the success with the given id according to the provided regexp.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
try:
self.db.change(channel, id, replacer)
irc.replySuccess()
except KeyError:
irc.error('There is no success #%s.' % id)
change = wrap(change, ['user', 'channeldb',
('id', 'success'), 'regexpReplacer'])
def stats(self, irc, msg, args, channel):
"""[<channel>]
Returns the number of successes in the success database. <channel> is
only necessary if the message isn't sent in the channel itself.
"""
num = self.db.size(channel)
irc.reply('There %s %s in my database.' %
(utils.be(num), utils.nItems('success', num)))
stats = wrap(stats, ['channeldb'])
Class = Success Class = Success

View File

@ -40,15 +40,18 @@ import math
import sets import sets
import time import time
import random import random
import fnmatch
import os.path import os.path
import UserDict import UserDict
import threading import threading
import supybot.cdb as cdb
import supybot.log as log import supybot.log as log
import supybot.dbi as dbi
import supybot.conf as conf import supybot.conf as conf
import supybot.ircdb as ircdb
import supybot.utils as utils import supybot.utils as utils
import supybot.world as world import supybot.world as world
from supybot.commands import *
import supybot.ircutils as ircutils import supybot.ircutils as ircutils
import supybot.webutils as webutils import supybot.webutils as webutils
import supybot.callbacks as callbacks import supybot.callbacks as callbacks
@ -194,7 +197,7 @@ class ChannelDBHandler(object):
def makeDb(self, filename): def makeDb(self, filename):
"""Override this to create your databases.""" """Override this to create your databases."""
return cdb.shelf(filename) raise NotImplementedError
def getDb(self, channel): def getDb(self, channel):
"""Use this to get a database for a specific channel.""" """Use this to get a database for a specific channel."""
@ -352,12 +355,173 @@ class ChannelUserDB(ChannelUserDictionary):
raise NotImplementedError raise NotImplementedError
## class ChannelIdDatabasePlugin(callbacks.Privmsg): class ChannelIdDatabasePlugin(callbacks.Privmsg):
## def __init__(self): class DB(DbiChannelDB):
## # XXX Register configuration variables. class DB(dbi.DB):
## self.__parent = super(ChannelIdDatabasePlugin, self) class Record(dbi.Record):
## self.__parent.__init__(self) __fields__ = [
## self.db = self.DB() 'at',
'by',
'text'
]
def add(self, at, by, text, **kwargs):
record = self.Record(at=at, by=by, text=text, **kwargs)
return super(self.__class__, self).add(record)
def __init__(self):
self.__parent = super(ChannelIdDatabasePlugin, self)
self.__parent.__init__()
self.db = DB(self.name(), {'flat': self.DB})()
def die(self):
self.db.close()
self.__parent.die()
def getCommandHelp(self, name):
help = self.__parent.getCommandHelp(name)
help = help.replace('$Types', utils.pluralize(self.name()))
help = help.replace('$Type', self.name())
help = help.replace('$types', utils.pluralize(self.name().lower()))
help = help.replace('$type', self.name().lower())
return help
def noSuchRecord(self, irc, channel, id):
irc.error('There is no %s with id #%s in my database for %s.' %
(self.name(), id, channel))
def checkChangeAllowed(self, irc, msg, channel, user, record):
if user.id == record.by:
return True
cap = ircdb.makeChannelCapability(channel, 'op')
if ircdb.checkCapability(msg.prefix, cap):
return True
irc.errorNoCapability(cap)
def addValidator(self, irc, text):
"""This should irc.error or raise an exception if text is invalid."""
pass
def add(self, irc, msg, args, user, channel, text):
"""[<channel>] <text>
Adds <text> to the $type database for <channel>.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
at = time.time()
self.addValidator(irc, text)
if text is not None:
id = self.db.add(channel, at, user.id, text)
irc.replySuccess('%s #%s added.' % (self.name(), id))
add = wrap(add, ['user', 'channeldb', 'text'])
def remove(self, irc, msg, args, user, channel, id):
"""[<channel>] <id>
Removes the $type with id <id> from the $type database for <channel>.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
try:
record = self.db.get(channel, id)
self.checkChangeAllowed(irc, msg, channel, user, record)
self.db.remove(channel, id)
irc.replySuccess()
except KeyError:
self.noSuchRecord(irc, channel, id)
remove = wrap(remove, ['user', 'channeldb', 'id'])
def searchSerializeRecord(self, record):
text = utils.quoted(utils.ellipsisify(record.text, 50))
return '#%s: %s' % (record.id, text)
def search(self, irc, msg, args, channel, optlist, glob):
"""[<channel>] [--{regexp,by} <value>] [<glob>]
Searches for $types matching the criteria given. XXX
"""
predicates = []
def p(record):
for predicate in predicates:
if not predicate(record):
return False
return True
for (opt, arg) in optlist:
if opt == 'by':
predicates.append(lambda r, arg=arg: r.by == arg.id)
elif opt == 'regexp':
predicates.append(lambda r, arg=arg: arg.search(r.text))
if glob:
# XXX Case sensitive.
predicates.append(lambda r: fnmatch.fnmatch(r.text, glob))
L = []
for record in self.db.select(channel, p):
L.append(self.searchSerializeRecord(record))
if L:
L.sort()
irc.reply(utils.commaAndify(L))
else:
irc.reply('No matching %s were found.' %
utils.pluralize(self.name().lower()))
search = wrap(search, ['channeldb',
getopts({'by': 'otherUser',
'regexp': 'regexpMatcher'}),
additional(rest('glob'))])
def showRecord(self, record):
try:
name = ircdb.users.getUser(record.by).name
except KeyError:
name = 'a user that is no longer registered'
at = time.localtime(record.at)
timeS = time.strftime(conf.supybot.humanTimestampFormat(), at)
return '%s #%S: %s (added by %s at %s)' % \
(self.name(), record.id, utils.quoted(record.text), name, timeS)
def get(self, irc, msg, args, channel, id):
"""[<channel>] <id>
Gets the $type with id <id> from the $type database for <channel>.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
try:
record = self.db.get(channel, id)
irc.reply(self.showRecord(record))
except KeyError:
self.noSuchRecord(irc, channel, id)
get = wrap(get, ['channeldb', 'id'])
def change(self, irc, msg, args, user, channel, id, replacer):
"""[<channel>] <id> <regexp>
Changes the $type with id <id> according to the regular expression
<regexp>. <channel> is only necessary if the message isn't sent in the
channel itself.
"""
try:
record = self.db.get(channel, id)
self.checkChangeAllowed(irc, msg, channel, user, record)
record.text = replacer(record.text)
self.db.set(channel, id, record)
irc.replySuccess()
except KeyError:
self.noSuchRecord(irc, channel, id)
change = wrap(change, ['user', 'channeldb', 'id', 'regexpReplacer'])
def stats(self, irc, msg, args, channel):
"""[<channel>]
Returns the number of $types in the database for <channel>.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
n = self.db.size(channel)
irc.reply('There %s %s in my database.' %
(utils.be(n), utils.nItems(self.name().lower(), n)))
stats = wrap(stats, ['channeldb'])
class PeriodicFileDownloader(object): class PeriodicFileDownloader(object):
"""A class to periodically download a file/files. """A class to periodically download a file/files.