Markov: ported to ircbot, noted last TODO. deleted
This commit is contained in:
parent
a345f4567e
commit
7876ad2290
1
TODO
1
TODO
@ -2,6 +2,7 @@ dr.botzo --- TODO
|
||||
|
||||
Both a reminder to myself and a hint to anyone else who wants to hack around...
|
||||
|
||||
* markov random chatter, add it back. should just need a thread and some polling
|
||||
* module unloading is *totally* busted. random objects disappear on an unload
|
||||
* some sort of cron interface (periodic events)
|
||||
* thinking about removing this, wouldn't crontab + XML-RPC be enough?
|
||||
|
@ -1,235 +0,0 @@
|
||||
"""
|
||||
Markov - Chatterbot via Markov chains for IRC
|
||||
Copyright (C) 2010 Brian S. Stephan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import random
|
||||
import re
|
||||
import thread
|
||||
import time
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
import markov.lib as markovlib
|
||||
from markov.models import MarkovContext, MarkovState, MarkovTarget
|
||||
|
||||
from extlib import irclib
|
||||
|
||||
from Module import Module
|
||||
|
||||
class Markov(Module):
|
||||
|
||||
"""Create a chatterbot very similar to a MegaHAL, but simpler and
|
||||
implemented in pure Python. Proof of concept code from Ape.
|
||||
|
||||
Ape wrote: based on this:
|
||||
http://uswaretech.com/blog/2009/06/pseudo-random-text-markov-chains-python/
|
||||
and this:
|
||||
http://code.activestate.com/recipes/194364-the-markov-chain-algorithm/
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, irc, config):
|
||||
"""Create the Markov chainer, and learn text from a file if
|
||||
available.
|
||||
|
||||
"""
|
||||
|
||||
# set up regexes, for replying to specific stuff
|
||||
learnpattern = '^!markov\s+learn\s+(.*)$'
|
||||
replypattern = '^!markov\s+reply(\s+min=(\d+))?(\s+max=(\d+))?(\s+(.*)$|$)'
|
||||
|
||||
self.learnre = re.compile(learnpattern)
|
||||
self.replyre = re.compile(replypattern)
|
||||
|
||||
self.shut_up = False
|
||||
self.lines_seen = []
|
||||
|
||||
Module.__init__(self, irc, config)
|
||||
|
||||
self.next_shut_up_check = 0
|
||||
self.next_chatter_check = 0
|
||||
thread.start_new_thread(self.thread_do, ())
|
||||
|
||||
def register_handlers(self):
|
||||
"""Handle pubmsg/privmsg, to learn and/or reply to IRC events."""
|
||||
|
||||
self.irc.server.add_global_handler('pubmsg', self.on_pub_or_privmsg,
|
||||
self.priority())
|
||||
self.irc.server.add_global_handler('privmsg', self.on_pub_or_privmsg,
|
||||
self.priority())
|
||||
self.irc.server.add_global_handler('pubmsg',
|
||||
self.learn_from_irc_event)
|
||||
self.irc.server.add_global_handler('privmsg',
|
||||
self.learn_from_irc_event)
|
||||
|
||||
def unregister_handlers(self):
|
||||
self.irc.server.remove_global_handler('pubmsg',
|
||||
self.on_pub_or_privmsg)
|
||||
self.irc.server.remove_global_handler('privmsg',
|
||||
self.on_pub_or_privmsg)
|
||||
self.irc.server.remove_global_handler('pubmsg',
|
||||
self.learn_from_irc_event)
|
||||
self.irc.server.remove_global_handler('privmsg',
|
||||
self.learn_from_irc_event)
|
||||
|
||||
def learn_from_irc_event(self, connection, event):
|
||||
"""Learn from IRC events."""
|
||||
|
||||
what = ''.join(event.arguments()[0])
|
||||
my_nick = connection.get_nickname()
|
||||
what = re.sub('^' + my_nick + '[:,]\s+', '', what)
|
||||
target = event.target()
|
||||
nick = irclib.nm_to_n(event.source())
|
||||
|
||||
if not irclib.is_channel(target):
|
||||
target = nick
|
||||
|
||||
self.lines_seen.append((nick, datetime.now()))
|
||||
|
||||
# don't learn from commands
|
||||
if self.learnre.search(what) or self.replyre.search(what):
|
||||
return
|
||||
|
||||
if not event._recursing:
|
||||
context = markovlib.get_or_create_target_context(target)
|
||||
markovlib.learn_line(what, context)
|
||||
|
||||
def do(self, connection, event, nick, userhost, what, admin_unlocked):
|
||||
"""Handle commands and inputs."""
|
||||
|
||||
target = event.target()
|
||||
|
||||
if self.learnre.search(what):
|
||||
return self.irc.reply(event, self.markov_learn(event,
|
||||
nick, userhost, what, admin_unlocked))
|
||||
elif self.replyre.search(what) and not self.shut_up:
|
||||
return self.irc.reply(event, self.markov_reply(event,
|
||||
nick, userhost, what, admin_unlocked))
|
||||
|
||||
if not self.shut_up:
|
||||
# not a command, so see if i'm being mentioned
|
||||
if re.search(connection.get_nickname(), what, re.IGNORECASE) is not None:
|
||||
context = markovlib.get_or_create_target_context(target)
|
||||
|
||||
addressed_pattern = '^' + connection.get_nickname() + '[:,]\s+(.*)'
|
||||
addressed_re = re.compile(addressed_pattern)
|
||||
if addressed_re.match(what):
|
||||
# i was addressed directly, so respond, addressing
|
||||
# the speaker
|
||||
topics = [x for x in addressed_re.match(what).group(1).split(' ') if len(x) >= 3]
|
||||
|
||||
self.lines_seen.append(('.self.said.', datetime.now()))
|
||||
return self.irc.reply(event, u"{0:s}: {1:s}".format(nick,
|
||||
u" ".join(markovlib.generate_line(context, topics=topics, max_sentences=1))))
|
||||
else:
|
||||
# i wasn't addressed directly, so just respond
|
||||
topics = [x for x in what.split(' ') if len(x) >= 3]
|
||||
self.lines_seen.append(('.self.said.', datetime.now()))
|
||||
|
||||
return self.irc.reply(event, u"{0:s}".format(u" ".join(markovlib.generate_line(context,
|
||||
topics=topics,
|
||||
max_sentences=1))))
|
||||
|
||||
def markov_learn(self, event, nick, userhost, what, admin_unlocked):
|
||||
"""Learn one line, as provided to the command."""
|
||||
|
||||
target = event.target()
|
||||
|
||||
if not irclib.is_channel(target):
|
||||
target = nick
|
||||
|
||||
match = self.learnre.search(what)
|
||||
if match:
|
||||
line = match.group(1)
|
||||
context = markovlib.get_or_create_target_context(target)
|
||||
markovlib.learn_line(line, context)
|
||||
|
||||
# return what was learned, for weird chaining purposes
|
||||
return line
|
||||
|
||||
def markov_reply(self, event, nick, userhost, what, admin_unlocked):
|
||||
"""Generate a reply to one line, without learning it."""
|
||||
|
||||
target = event.target()
|
||||
|
||||
if not irclib.is_channel(target):
|
||||
target = nick
|
||||
|
||||
match = self.replyre.search(what)
|
||||
if match:
|
||||
min_size = 15
|
||||
max_size = 30
|
||||
context = markovlib.get_or_create_target_context(target)
|
||||
|
||||
if match.group(2):
|
||||
min_size = int(match.group(2))
|
||||
if match.group(4):
|
||||
max_size = int(match.group(4))
|
||||
|
||||
if match.group(5) != '':
|
||||
line = match.group(6)
|
||||
topics = [x for x in line.split(' ') if len(x) >= 3]
|
||||
|
||||
self.lines_seen.append(('.self.said.', datetime.now()))
|
||||
return u" ".join(markovlib.generate_line(context, topics=topics,
|
||||
min_words=min_size, max_words=max_size,
|
||||
max_sentences=1))
|
||||
else:
|
||||
self.lines_seen.append(('.self.said.', datetime.now()))
|
||||
return u" ".join(markovlib.generate_line(context, min_words=min_size,
|
||||
max_words=max_size,
|
||||
max_sentences=1))
|
||||
|
||||
def thread_do(self):
|
||||
"""Do various things."""
|
||||
|
||||
while not self.is_shutdown:
|
||||
self._do_shut_up_checks()
|
||||
self._do_random_chatter_check()
|
||||
time.sleep(1)
|
||||
|
||||
def _do_random_chatter_check(self):
|
||||
"""Randomly say something to a channel."""
|
||||
|
||||
# TODO: make this do stuff again
|
||||
return
|
||||
|
||||
def _do_shut_up_checks(self):
|
||||
"""Check to see if we've been talking too much, and shut up if so."""
|
||||
|
||||
if self.next_shut_up_check < time.time():
|
||||
self.shut_up = False
|
||||
self.next_shut_up_check = time.time() + 30
|
||||
|
||||
last_30_sec_lines = []
|
||||
|
||||
for (nick, then) in self.lines_seen:
|
||||
rdelta = relativedelta(datetime.now(), then)
|
||||
if (rdelta.years == 0 and rdelta.months == 0 and rdelta.days == 0 and
|
||||
rdelta.hours == 0 and rdelta.minutes == 0 and rdelta.seconds <= 29):
|
||||
last_30_sec_lines.append((nick, then))
|
||||
|
||||
if len(last_30_sec_lines) >= 8:
|
||||
lines_i_said = len(filter(lambda (a, b): a == '.self.said.', last_30_sec_lines))
|
||||
if lines_i_said >= 8:
|
||||
self.shut_up = True
|
||||
targets = self._get_chatter_targets()
|
||||
for t in targets:
|
||||
self.sendmsg(t['target'],
|
||||
'shutting up for 30 seconds due to last 30 seconds of activity')
|
Loading…
Reference in New Issue
Block a user