Reverted my accidental clobbering.

This commit is contained in:
Jeremy Fincher 2004-07-26 19:58:47 +00:00
parent 1b5e3b82f8
commit 2fe7bd8c7a

View File

@ -30,8 +30,8 @@
### ###
""" """
Silently listens to a channel, building an SQL database of Markov Chains for Silently listens to a channel, building a database of Markov Chains for later
later hijinks. To read more about Markov Chains, check out hijinks. To read more about Markov Chains, check out
<http://www.cs.bell-labs.com/cm/cs/pearls/sec153.html>. When the database is <http://www.cs.bell-labs.com/cm/cs/pearls/sec153.html>. When the database is
large enough, you can have it make fun little random messages from it. large enough, you can have it make fun little random messages from it.
""" """
@ -40,109 +40,112 @@ __revision__ = "$Id$"
import plugins import plugins
import anydbm
import random
import os.path import os.path
import conf
import world
import ircmsgs import ircmsgs
import ircutils import ircutils
import privmsgs import privmsgs
import callbacks import callbacks
class MarkovDB(object):
def __init__(self):
self.dbs = {}
def die(self):
for db in self.dbs.values():
try: try:
import sqlite db.close()
except ImportError: except:
raise callbacks.Error, 'You need to have PySQLite installed to use this ' \ continue
'plugin. Download it at <http://pysqlite.sf.net/>'
conf.registerPlugin('Markov') def _getDb(self, channel):
conf.registerGlobalValue(supybot.plugins.Markov, 'maximumWorkQueue', channel = channel.lower()
registry.PositiveInteger(10, """Determines what the maximum number of if channel not in self.dbs:
oustanding Markov work requests will be allowed.""")) filename = '%s-Markov.db' % channel
filename = os.path.join(conf.supybot.directories.data(), filename)
self.dbs[channel] = anydbm.open(filename, 'c')
return self.dbs[channel]
class MarkovThread(threading.Thread): def __getitem__(self, (channel, item)):
def __init__(self, high, low): return self._getDb(channel)[item]
self.q = q
def run(self): def __setitem__(self, (channel, item), value):
while 1: self._getDb(channel)[item] = value
f = self.q.get()
if f is None: def getNumberOfPairs(self, channel):
break try:
else: # Minus one, because we have a key storing the first pairs.
f() return len(self[channel.lower()]) - 1
except KeyError:
return 0
def getNumberOfFirstPairs(self, channel):
try:
return len(self[channel, ''].split())
except KeyError:
return 0
def getFirstPair(self, channel):
try:
pairs = self[channel, ''].split()
except KeyError:
raise ValueError('No starting pairs in the database.')
pair = random.choice(pairs)
return pair.split('\x00', 1)
def getFollower(self, channel, first, second):
pair = '%s %s' % (first, second)
try:
followers = self[channel, pair].split()
except KeyError:
return '\x00'
return random.choice(followers)
def addFirstPair(self, channel, first, second):
pair = '%s\x00%s' % (first, second)
try:
startingPairs = self[channel, '']
except KeyError:
startingPairs = ''
self[channel, ''] = '%s%s ' % (startingPairs, pair)
def addPair(self, channel, first, second, follower):
pair = '%s %s' % (first, second)
try:
followers = self[channel, pair]
except KeyError:
followers = ''
self[channel, pair] = '%s%s ' % (followers, follower)
class Markov(plugins.ChannelDBHandler, callbacks.Privmsg): class Markov(callbacks.Privmsg):
def __init__(self): def __init__(self):
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
plugins.ChannelDBHandler.__init__(self) self.db = MarkovDB()
self.q = Queue.Queue(self.registryValue('maximumWorkQueue'))
self.t = MarkovThread(self.q)
self.t.start()
def putq(self, f):
try:
self.q.put(f, False)
except Queue.Full:
self.log.info('Can\'t putq, queue full.')
raise
def makeDb(self, filename):
def f():
if os.path.exists(filename):
return sqlite.connect(filename)
db = sqlite.connect(filename)
cursor = db.cursor()
cursor.execute("""CREATE TABLE pairs (
id INTEGER PRIMARY KEY,
first TEXT,
second TEXT,
is_first BOOLEAN,
UNIQUE (first, second) ON CONFLICT IGNORE
)""")
cursor.execute("""CREATE TABLE follows (
id INTEGER PRIMARY KEY,
pair_id INTEGER,
word TEXT
)""")
cursor.execute("""CREATE INDEX follows_pair_id
ON follows (pair_id)""")
db.commit()
return db
self.putq(f)
def doPrivmsg(self, irc, msg): def doPrivmsg(self, irc, msg):
if not ircutils.isChannel(msg.args[0]): if not ircutils.isChannel(msg.args[0]):
return return
channel = msg.args[0] channel = msg.args[0]
db = self.getDb(channel)
cursor = db.cursor()
if ircmsgs.isAction(msg): if ircmsgs.isAction(msg):
words = ircmsgs.unAction(msg).split() words = ircmsgs.unAction(msg).split()
words.insert(0, '\x00nick')
#words.insert(0, msg.nick)
else: else:
words = msg.args[1].split() words = msg.args[1].split()
isFirst = True isFirst = True
for (first, second, follower) in window(words, 3): for (first, second, follower) in window(words, 3):
if isFirst: if isFirst:
cursor.execute("""INSERT OR REPLACE self.db.addFirstPair(channel, first, second)
INTO pairs VALUES (NULL, %s, %s, 1)""",
first, second)
isFirst = False isFirst = False
else: self.db.addPair(channel, first, second, follower)
cursor.execute("INSERT INTO pairs VALUES (NULL, %s, %s, 0)",
first, second)
cursor.execute("""SELECT id FROM pairs
WHERE first=%s AND second=%s""", first, second)
id = int(cursor.fetchone()[0])
cursor.execute("""INSERT INTO follows VALUES (NULL, %s, %s)""",
id, follower)
if not isFirst: # i.e., if the loop iterated at all. if not isFirst: # i.e., if the loop iterated at all.
cursor.execute("""INSERT INTO pairs VALUES (NULL, %s, %s, 0)""", self.db.addPair(channel, second, follower, '\x00')
second, follower)
cursor.execute("""SELECT id FROM pairs
WHERE first=%s AND second=%s""", second,follower)
id = int(cursor.fetchone()[0])
cursor.execute("INSERT INTO follows VALUES (NULL, %s, NULL)", id)
db.commit()
_maxMarkovLength = 80 _maxMarkovLength = 80
_minMarkovLength = 7 _minMarkovLength = 7
@ -153,40 +156,25 @@ class Markov(plugins.ChannelDBHandler, callbacks.Privmsg):
data kept on <channel> (which is only necessary if not sent in the data kept on <channel> (which is only necessary if not sent in the
channel itself). channel itself).
""" """
argsCopy = args[:]
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)
db = self.getDb(channel) try:
cursor = db.cursor() pair = self.db.getFirstPair(channel)
words = [] except ValueError:
cursor.execute("""SELECT id, first, second FROM pairs irc.error('I have no records for this channel.')
WHERE is_first=1
ORDER BY random()
LIMIT 1""")
if cursor.rowcount == 0:
irc.error('I have no records for that channel.')
return return
(id, first, second) = cursor.fetchone() words = [pair[0], pair[1]]
id = int(id)
words.append(first)
words.append(second)
sql = """SELECT follows.word FROM pairs, follows
WHERE pairs.first=%s AND
pairs.second=%s AND
pairs.id=follows.pair_id
ORDER BY random()
LIMIT 1"""
while len(words) < self._maxMarkovLength: while len(words) < self._maxMarkovLength:
cursor.execute(sql, words[-2], words[-1]) follower = self.db.getFollower(channel, words[-2], words[-1])
results = cursor.fetchone() if follower == '\x00':
if not results:
break
word = results[0]
if word is None:
break
words.append(word)
if len(words) < self._minMarkovLength: if len(words) < self._minMarkovLength:
self.markov(irc, msg, argsCopy) pair = self.db.getFirstPair(channel)
words = [pair[0], pair[1]]
else: else:
break
else:
words.append(follower)
if words[0] == '\x00nick':
words[0] = choice(irc.state.channels[channel].users)
irc.reply(' '.join(words)) irc.reply(' '.join(words))
def pairs(self, irc, msg, args): def pairs(self, irc, msg, args):
@ -196,10 +184,7 @@ class Markov(plugins.ChannelDBHandler, callbacks.Privmsg):
<channel>. <channel>.
""" """
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)
db = self.getDb(channel) n = self.db.getNumberOfPairs(channel)
cursor = db.cursor()
cursor.execute("""SELECT COUNT(*) FROM pairs""")
n = int(cursor.fetchone()[0])
s = 'There are %s pairs in my Markov database for %s' % (n, channel) s = 'There are %s pairs in my Markov database for %s' % (n, channel)
irc.reply(s) irc.reply(s)
@ -210,40 +195,37 @@ class Markov(plugins.ChannelDBHandler, callbacks.Privmsg):
<channel>. <channel>.
""" """
channel = privmsgs.getChannel(msg, args) channel = privmsgs.getChannel(msg, args)
db = self.getDb(channel) n = self.db.getNumberOfFirstPairs(channel)
cursor = db.cursor()
cursor.execute("""SELECT COUNT(*) FROM pairs WHERE is_first=1""")
n = int(cursor.fetchone()[0])
s = 'There are %s first pairs in my Markov database for %s'%(n,channel) s = 'There are %s first pairs in my Markov database for %s'%(n,channel)
irc.reply(s) irc.reply(s)
def follows(self, irc, msg, args): # def follows(self, irc, msg, args):
"""[<channel>] # """[<channel>]
#
# Returns the number of Markov's third links in the database for
# <channel>.
# """
# channel = privmsgs.getChannel(msg, args)
# db = self._getDb(channel)
# cursor = db.cursor()
# cursor.execute("""SELECT COUNT(*) FROM follows""")
# n = int(cursor.fetchone()[0])
# s = 'There are %s follows in my Markov database for %s' % (n, channel)
# irc.reply(s)
Returns the number of Markov's third links in the database for # def lasts(self, irc, msg, args):
<channel>. # """[<channel>]
""" #
channel = privmsgs.getChannel(msg, args) # Returns the number of Markov's last links in the database for
db = self.getDb(channel) # <channel>.
cursor = db.cursor() # """
cursor.execute("""SELECT COUNT(*) FROM follows""") # channel = privmsgs.getChannel(msg, args)
n = int(cursor.fetchone()[0]) # db = self._getDb(channel)
s = 'There are %s follows in my Markov database for %s' % (n, channel) # cursor = db.cursor()
irc.reply(s) # cursor.execute("""SELECT COUNT(*) FROM follows WHERE word ISNULL""")
# n = int(cursor.fetchone()[0])
def lasts(self, irc, msg, args): # s = 'There are %s lasts in my Markov database for %s' % (n, channel)
"""[<channel>] # irc.reply(s)
Returns the number of Markov's last links in the database for
<channel>.
"""
channel = privmsgs.getChannel(msg, args)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT COUNT(*) FROM follows WHERE word ISNULL""")
n = int(cursor.fetchone()[0])
s = 'There are %s lasts in my Markov database for %s' % (n, channel)
irc.reply(s)
Class = Markov Class = Markov