diff --git a/plugins/ChannelDB.py b/plugins/ChannelDB.py
index 2a7a84cbd..2d2b98463 100644
--- a/plugins/ChannelDB.py
+++ b/plugins/ChannelDB.py
@@ -52,10 +52,10 @@ import utils
import ircdb
import ircmsgs
import plugins
-import privmsgs
import ircutils
+import privmsgs
+import registry
import callbacks
-import configurable
try:
import sqlite
@@ -64,51 +64,38 @@ except ImportError:
'plugin. Download it at '
# I should write/copy a generalized proxy at some point.
-class ReWrapper(object):
- def __init__(self, L):
- self.s = utils.dqrepr(' '.join(L))
- self.r = re.compile('|'.join(imap(re.escape, L)))
+class Smileys(registry.Value):
+ def set(self, s):
+ L = s.split()
+ self.s = s
+ self.setValue(L)
- def findall(self, *args, **kwargs):
- return self.r.findall(*args, **kwargs)
+ def setValue(self, v):
+ self.value = re.compile('|'.join(imap(re.escape, v)))
def __str__(self):
return self.s
- __repr__ = __str__
-
-def SmileyType(s):
- try:
- L = configurable.SpaceSeparatedStrListType(s)
- return ReWrapper(L)
- except configurable.Error:
- raise configurable.Error, 'Value must be a space-separated list of ' \
- 'smileys or frowns.'
-
+conf.registerPlugin('ChannelDB')
+conf.registerChannelValue(conf.supybot.plugins.ChannelDB, 'selfStats',
+ registry.Boolean(True, """Determines whether the bot will keep channel
+ statistics on itself, possibly skewing the channel stats (especially in
+ cases where the bot is relaying between channels on a network)."""))
+conf.registerChannelValue(conf.supybot.plugins.ChannelDB, 'smileys',
+ Smileys(':) ;) ;] :-) :-D :D :P :p (= =)'.split(), """Determines what
+ words (i.e., pieces of text with no spaces in them) are considered
+ 'smileys' for the purposes of stats-keeping."""))
+conf.registerChannelValue(conf.supybot.plugins.ChannelDB, 'frowns',
+ Smileys(':| :-/ :-\\ :\\ :/ :( :-( :\'('.split(), """Determines what words
+ (i.e., pieces of text with no spaces in them ) are considered 'frowns' for
+ the purposes of stats-keeping."""))
+
+
class ChannelDB(plugins.ChannelDBHandler,
- configurable.Mixin,
callbacks.Privmsg):
noIgnore = True
- configurables = configurable.Dictionary(
- [('self-stats', configurable.BoolType, True,
- """Determines whether the bot will keep channel statistics on itself,
- possibly skewing the channel stats (especially in cases where he's
- relaying between channels on a network."""),
- ('wordstats-top-n', configurable.IntType, 3,
- """Determines the maximum number of top users to show for a given
- wordstat when you request the wordstats for a particular word."""),
- ('smileys', SmileyType, SmileyType(':) ;) ;] :-) :-D :D :P :p (= =)'),
- """Determines what words count as smileys for the purpose of keeping
- statistics on the number of smileys each user has sent to the
- channel."""),
- ('frowns', SmileyType, SmileyType(':| :-/ :-\\ :\\ :/ :( :-( :\'('),
- """Determines what words count as frowns for the purpose of keeping
- statistics on the number of frowns each user has sent ot the
- channel."""),]
- )
def __init__(self):
callbacks.Privmsg.__init__(self)
- configurable.Mixin.__init__(self)
plugins.ChannelDBHandler.__init__(self)
self.lastmsg = None
self.laststate = None
@@ -116,7 +103,6 @@ class ChannelDB(plugins.ChannelDBHandler,
def die(self):
callbacks.Privmsg.die(self)
- configurable.Mixin.die(self)
plugins.ChannelDBHandler.die(self)
def makeDb(self, filename):
@@ -167,21 +153,6 @@ class ChannelDB(plugins.ChannelDBHandler,
cursor.execute("""INSERT INTO channel_stats
VALUES (0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0)""")
- cursor.execute("""CREATE TABLE words (
- id INTEGER PRIMARY KEY,
- word TEXT UNIQUE ON CONFLICT IGNORE
- )""")
- cursor.execute("""CREATE TABLE word_stats (
- id INTEGER PRIMARY KEY,
- word_id INTEGER,
- user_id INTEGER,
- count INTEGER,
- UNIQUE (word_id, user_id) ON CONFLICT IGNORE
- )""")
- cursor.execute("""CREATE INDEX word_stats_word_id
- ON word_stats (word_id)""")
- cursor.execute("""CREATE INDEX word_stats_user_id
- ON word_stats (user_id)""")
db.commit()
def p(s1, s2):
return int(ircutils.nickEqual(s1, s2))
@@ -199,48 +170,16 @@ class ChannelDB(plugins.ChannelDBHandler,
super(ChannelDB, self).__call__(irc, msg)
def doPrivmsg(self, irc, msg):
- if ircutils.isChannel(msg.args[0]):
- self._updatePrivmsgStats(msg)
- self._updateWordStats(msg)
-
- _alphanumeric = string.ascii_letters + string.digits
- _nonAlphanumeric = string.ascii.translate(string.ascii, _alphanumeric)
- def _updateWordStats(self, msg):
- try:
- if self.outFiltering:
- id = 0
- else:
- id = ircdb.users.getUserId(msg.prefix)
- except KeyError:
+ if not ircutils.isChannel(msg.args[0]):
return
(channel, s) = msg.args
- s = s.strip()
- if not s:
- return
- db = self.getDb(channel)
- cursor = db.cursor()
- words = s.lower().split()
- words = [s.strip(self._nonAlphanumeric) for s in words]
- criteria = ['word=%s'] * len(words)
- criterion = ' OR '.join(criteria)
- cursor.execute("SELECT id, word FROM words WHERE %s"%criterion, *words)
- for (wordId, word) in cursor.fetchall():
- cursor.execute("""INSERT INTO word_stats
- VALUES(NULL, %s, %s, 0)""", wordId, id)
- cursor.execute("""UPDATE word_stats SET count=count+%s
- WHERE word_id=%s AND user_id=%s""",
- words.count(word), wordId, id)
- db.commit()
-
- def _updatePrivmsgStats(self, msg):
- (channel, s) = msg.args
db = self.getDb(channel)
cursor = db.cursor()
chars = len(s)
words = len(s.split())
isAction = ircmsgs.isAction(msg)
- frowns = len(self.configurables.get('frowns', channel).findall(s))
- smileys = len(self.configurables.get('smileys', channel).findall(s))
+ frowns = len(self.registryValue('frowns', channel).findall(s))
+ smileys = len(self.registryValue('smileys', channel).findall(s))
s = ircmsgs.prettyPrint(msg)
cursor.execute("""UPDATE channel_stats
SET smileys=smileys+%s,
@@ -275,9 +214,7 @@ class ChannelDB(plugins.ChannelDBHandler,
def outFilter(self, irc, msg):
if msg.command == 'PRIVMSG':
if ircutils.isChannel(msg.args[0]):
- if self.configurables.get('self-stats', msg.args[0]):
- db = self.getDb(msg.args[0])
- cursor = db.cursor()
+ if self.registryValue('selfStats', msg.args[0]):
try:
self.outFiltering = True
self._updatePrivmsgStats(msg)
@@ -495,160 +432,6 @@ class ChannelDB(plugins.ChannelDBHandler,
values.kicks, values.modes, values.topics)
irc.reply(s)
- def addword(self, irc, msg, args):
- """[]
-
- Keeps stats on in . is only necessary if the
- message isn't sent in the channel itself.
- """
- channel = privmsgs.getChannel(msg, args)
- word = privmsgs.getArgs(args)
- word = word.strip()
- if word.strip(self._nonAlphanumeric) != word:
- irc.error(' must not contain non-alphanumeric chars.')
- return
- word = word.lower()
- db = self.getDb(channel)
- cursor = db.cursor()
- cursor.execute("""INSERT INTO words VALUES (NULL, %s)""", word)
- db.commit()
- irc.replySuccess()
-
- def wordstats(self, irc, msg, args):
- """[] [] []
-
- With no arguments, returns the list of words that are being monitored
- for stats. With alone, returns all the stats for that user.
- With alone, returns the top users for that word. With
- and , returns that user's stat for that word. is only
- needed if not said in the channel. (Note: if only one of or
- is given, is assumed first and only if no stats are
- available for that word, do we assume it's .)
- """
- channel = privmsgs.getChannel(msg, args)
- (arg1, arg2) = privmsgs.getArgs(args, required=0, optional=2)
- db = self.getDb(channel)
- cursor = db.cursor()
- if not arg1 and not arg2:
- cursor.execute("""SELECT word FROM words""")
- if cursor.rowcount == 0:
- irc.reply('I am not currently keeping any word stats.')
- return
- l = [repr(tup[0]) for tup in cursor.fetchall()]
- s = 'Currently keeping stats for: %s' % utils.commaAndify(l)
- irc.reply(s)
- elif arg1 and arg2:
- user, word = (arg1, arg2)
- try:
- id = ircdb.users.getUserId(user)
- except KeyError: # Maybe it was a nick. Check the hostmask.
- try:
- hostmask = irc.state.nickToHostmask(user)
- id = ircdb.users.getUserId(hostmask)
- except KeyError:
- irc.errorNoUser()
- return
- db = self.getDb(channel)
- cursor = db.cursor()
- word = word.lower()
- cursor.execute("""SELECT word_stats.count FROM words, word_stats
- WHERE words.word=%s AND
- word_id=words.id AND
- word_stats.user_id=%s""", word, id)
- if cursor.rowcount == 0:
- cursor.execute("""SELECT id FROM words WHERE word=%s""", word)
- if cursor.rowcount == 0:
- irc.error('I\'m not keeping stats on %r.' % word)
- else:
- irc.error('%s has never said %r.' % (user, word))
- return
- count = int(cursor.fetchone()[0])
- s = '%s has said %r %s.' % (user,word,utils.nItems('time', count))
- irc.reply(s)
- else:
- # Figure out if we got a user or a word
- cursor.execute("""SELECT word FROM words
- WHERE word=%s""", arg1)
- if cursor.rowcount == 0:
- # It was a user.
- try:
- id = ircdb.users.getUserId(arg1)
- except KeyError: # Maybe it was a nick. Check the hostmask.
- try:
- hostmask = irc.state.nickToHostmask(arg1)
- id = ircdb.users.getUserId(hostmask)
- except KeyError:
- irc.error('%r doesn\'t look like a user I know '
- 'or a word that I\'m keeping stats '
- 'on' % arg1)
- return
- cursor.execute("""SELECT words.word, word_stats.count
- FROM words, word_stats
- WHERE words.id = word_stats.word_id
- AND word_stats.user_id=%s
- ORDER BY words.word""", id)
- if cursor.rowcount == 0:
- username = ircdb.users.getUser(id).name
- irc.error('%r has no wordstats' % username)
- return
- L = [('%r: %s' % (word, count)) for
- (word, count) in cursor.fetchall()]
- irc.reply(utils.commaAndify(L))
- return
- else:
- # It's a word, not a user
- word = arg1
- numUsers = self.configurables.get('wordstats-top-n',
- msg.args[0])
- cursor.execute("""SELECT word_stats.count,
- word_stats.user_id
- FROM words, word_stats
- WHERE words.word=%s AND
- words.id=word_stats.word_id
- ORDER BY word_stats.count DESC""",
- word)
- if cursor.rowcount == 0:
- irc.error('No one has said %r' % word)
- return
- results = cursor.fetchall()
- numResultsShown = min(cursor.rowcount, numUsers)
- cursor.execute("""SELECT sum(word_stats.count)
- FROM words, word_stats
- WHERE words.word=%s AND
- words.id=word_stats.word_id""",
- word)
- total = int(cursor.fetchone()[0])
- ers = '%rer' % word
- ret = 'Top %s ' % utils.nItems(ers, numResultsShown)
- ret += '(out of a total of %s seen):' % \
- utils.nItems(repr(word), total)
- L = []
- for (count, id) in results[:numResultsShown]:
- username = ircdb.users.getUser(id).name
- L.append('%s: %s' % (username, count))
- try:
- id = ircdb.users.getUserId(msg.prefix)
- rank = 1
- s = "" # Don't say anything if they show in the output
- # already
- seenUser = False
- for (count, userId) in results:
- if userId == id:
- seenUser = True
- if rank > numResultsShown:
- s = 'You are ranked %s out of %s with %s.' % \
- (rank, utils.nItems(ers, len(results)),
- utils.nItems(repr(word), count))
- break
- else:
- rank += 1
- else:
- if not seenUser:
- s = 'You have not said %r' % word
- ret = '%s %s. %s' % (ret, utils.commaAndify(L), s)
- except KeyError:
- ret = '%s %s.' % (ret, utils.commaAndify(L))
- irc.reply(ret)
Class = ChannelDB
diff --git a/test/test_ChannelDB.py b/test/test_ChannelDB.py
index 79bc57b59..ad014d464 100644
--- a/test/test_ChannelDB.py
+++ b/test/test_ChannelDB.py
@@ -39,8 +39,8 @@ except ImportError:
sqlite = None
if sqlite is not None:
- class ChannelDBTestCase(ChannelPluginTestCase, PluginDocumentation):
- plugins = ('ChannelDB', 'Misc', 'User')
+ class ChannelDBTestCase(ChannelPluginTestCase):
+ plugins = ('ChannelDB', 'User')
def setUp(self):
ChannelPluginTestCase.setUp(self)
self.prefix = 'foo!bar@baz'
@@ -94,115 +94,6 @@ if sqlite is not None:
def testSeenNoUser(self):
self.assertNotRegexp('seen --user alsdkfjalsdfkj', 'KeyError')
- def testWordStatsNoArgs(self):
- self.assertResponse('wordstats', 'I am not currently keeping any '
- 'word stats.')
- self.assertNotError('addword lol')
- self.assertResponse('wordstats', 'Currently keeping stats for: '
- '\'lol\'')
-
- def testWordStatsUser(self):
- self.assertNotError('addword lol')
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'lol',
- prefix=self.prefix))
- self.assertResponse('wordstats foo', '\'lol\': 2')
- self.assertNotError('addword moo')
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'moo',
- prefix=self.prefix))
- self.assertResponse('wordstats foo', '\'lol\': 2 and \'moo\': 2')
-
- def testWordStatsWord(self):
- userPrefix1 = 'moo!bar@baz'; userNick1 = 'moo'
- userPrefix2 = 'boo!bar@baz'; userNick2 = 'boo'
- self.irc.feedMsg(ircmsgs.privmsg(self.irc.nick,
- 'register %s bar' % userNick1,
- prefix=userPrefix1))
- self.irc.feedMsg(ircmsgs.privmsg(self.irc.nick,
- 'register %s bar' % userNick2,
- prefix=userPrefix2))
- _ = self.irc.takeMsg()
- _ = self.irc.takeMsg()
- self.assertNotError('addword lol')
- self.assertRegexp('wordstats lol', 'foo: 1')
- for i in range(5):
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'lol',
- prefix=userPrefix1))
- self.assertRegexp('wordstats lol',
- '2.*%s: 5.*foo: 2' % userNick1)
- for i in range(10):
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'lol',
- prefix=userPrefix2))
- self.assertRegexp('wordstats lol',
- '3.*%s: 10.*%s: 5.*foo: 3' %
- (userNick2, userNick1))
- # Check for the extra-swanky stuff too
- # (note: to do so we must make sure they don't appear in the list,
- # so we'll tweak the config)
- self.assertNotError('channeldb config wordstats-top-n 2')
- self.assertRegexp('wordstats lol',
- 'total.*19 \'lol\'s.*%s: 10.*%s: 5.*'
- 'ranked 3 out of 3 \'lol\'ers' % \
- (userNick2, userNick1))
-
- def testWordStatsUserWord(self):
- self.assertNotError('addword lol')
- self.assertResponse('wordstats foo lol',
- 'foo has said \'lol\' 1 time.')
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'lol',
- prefix=self.prefix))
- self.assertResponse('wordstats foo lol',
- 'foo has said \'lol\' 3 times.')
- # Now check for case-insensitivity
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'LOL',
- prefix=self.prefix))
- self.assertResponse('wordstats foo lol',
- 'foo has said \'lol\' 5 times.')
- # Check and make sure actions get nabbed too
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'lol',
- prefix=self.prefix))
- self.assertResponse('wordstats foo lol',
- 'foo has said \'lol\' 7 times.')
- # Check and make sure it handles two words in one message
- self.assertNotError('addword heh')
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'lol heh',
- prefix=self.prefix))
- self.assertResponse('wordstats foo lol',
- 'foo has said \'lol\' 9 times.')
- self.assertResponse('wordstats foo heh',
- 'foo has said \'heh\' 2 times.')
- # It should ignore punctuation around words
- self.irc.feedMsg(ircmsgs.privmsg(self.channel,'lol, I said "heh"',
- prefix=self.prefix))
- self.assertResponse('wordstats foo lol',
- 'foo has said \'lol\' 11 times.')
- self.assertResponse('wordstats foo heh',
- 'foo has said \'heh\' 4 times.')
-
- def testAddword(self):
- self.assertError('addword lol!')
- self.assertNotError('addword lolz0r')
-
- def testWordStatsTopN(self):
- self.assertNotError('addword lol')
- self.assertNotError('channeldb config wordstats-top-n 5')
- # Create 10 users and have them each send a different number of
- # 'lol's to the channel
- users = []
- for i in range(10):
- users.append(('foo%s!bar@baz' % i, 'foo%s' % i))
- self.irc.feedMsg(ircmsgs.privmsg(self.irc.nick,
- 'register %s bar' % \
- users[i][1],
- prefix=users[i][0]))
- _ = self.irc.takeMsg()
- for i in range(10):
- for j in range(i):
- self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'lol',
- prefix=users[i][0]))
- # Make sure it shows the top 5
- self.assertRegexp('wordstats lol',
- 'Top 5 \'lol\'ers.*foo9: 9.*foo8: 8.*'
- 'foo7: 7.*foo6: 6.*foo5: 5')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: