Compare commits

..

8 Commits

4 changed files with 94 additions and 14 deletions

View File

@ -60,15 +60,10 @@ class Markov(Plugin):
def handle_chatter(self, connection, event):
"""Learn from IRC chatter."""
what = event.arguments[0]
if connection.server_config.additional_addressed_nicks:
all_nicks = '|'.join(connection.server_config.additional_addressed_nicks.split('\n') +
[connection.get_nickname()])
else:
all_nicks = connection.get_nickname()
trimmed_what = re.sub(r'^(({nicks})[:,]|@({nicks}))\s+'.format(nicks=all_nicks), '', what)
nick = irc.client.NickMask(event.source).nick
who = irc.client.NickMask(event.source).nick
target = reply_destination_for_event(event)
log.debug("what: '%s', who: '%s', target: '%s'", what, who, target)
# check to see whether or not we should learn from this channel
channel = None
if irc.client.is_channel(target):
@ -78,11 +73,23 @@ class Markov(Plugin):
log.debug("not learning from %s as i've been told to ignore it", channel)
else:
# learn the line
# remove our own nick and aliases from what we learn
if connection.server_config.additional_addressed_nicks:
all_nicks = '|'.join(connection.server_config.additional_addressed_nicks.split('\n') +
[connection.get_nickname()])
else:
all_nicks = connection.get_nickname()
what = re.sub(r'^(({nicks})[:,]|@({nicks}))\s+'.format(nicks=all_nicks), '', what)
# don't learn the speaker's nick if this came over a bridge
if channel and who == channel.discord_bridge:
what = ' '.join(what.split(' ')[1:])
recursing = getattr(event, 'recursing', False)
if not recursing:
log.debug("learning %s", trimmed_what)
log.debug("learning %s", what)
context = markovlib.get_or_create_target_context(target)
markovlib.learn_line(trimmed_what, context)
markovlib.learn_line(what, context)
log.debug("searching '%s' for '%s'", what, all_nicks)
if re.search(all_nicks, what, re.IGNORECASE) is not None:
@ -96,7 +103,7 @@ class Markov(Plugin):
topics = [x for x in match.group('addressed_msg').split(' ') if len(x) >= 3]
return self.bot.reply(event, "{0:s}: {1:s}"
"".format(nick, " ".join(markovlib.generate_line(context, topics=topics))))
"".format(who, " ".join(markovlib.generate_line(context, topics=topics))))
else:
# i wasn't addressed directly, so just respond
topics = [x for x in what.split(' ') if len(x) >= 3]

View File

@ -1,6 +1,6 @@
"""Provide methods for manipulating markov chain processing."""
import logging
from random import SystemRandom as sysrand
import random
from django.db.models import Sum
@ -22,7 +22,7 @@ def generate_line(context, topics=None, min_words=15, max_words=30, sentence_bia
else:
if len(line) > 0:
if line[-1][-1] not in [',', '.', '!', '?', ':']:
line[-1] += sysrand.choice(['?', '.', '!'])
line[-1] += random.SystemRandom().choice(['?', '.', '!'])
tries += 1
@ -53,7 +53,7 @@ def generate_sentence(context, topics=None, min_words=15, max_words=30):
words = []
# if we have topics, try to work from it and work backwards
if topics:
topic_word = sysrand.choice(topics)
topic_word = random.SystemRandom().choice(topics)
topics.remove(topic_word)
log.debug("looking for topic '%s'", topic_word)
new_states = MarkovState.objects.filter(context=context, v=topic_word)
@ -100,7 +100,7 @@ def generate_sentence(context, topics=None, min_words=15, max_words=30):
words.append(MarkovState._stop)
elif len(target_hits) > 0:
# if there's a target word in the states, pick it
target_hit = sysrand.choice(target_hits)
target_hit = random.SystemRandom().choice(target_hits)
log.debug("found a topic hit %s, using it", target_hit)
topics.remove(target_hit)
words.append(target_hit)

View File

@ -0,0 +1,72 @@
"""Test IRC behavior of the markov plugin."""
from unittest import mock
from django.test import TestCase
from ircbot.models import IrcServer
from markov.ircplugin import Markov
class MarkovTestCase(TestCase):
"""Test the markov plugin."""
fixtures = ['tests/fixtures/irc_server_fixture.json']
def setUp(self):
"""Create common objects."""
self.mock_bot = mock.MagicMock()
self.mock_connection = mock.MagicMock()
self.mock_connection.get_nickname.return_value = 'test_bot'
self.mock_connection.server_config = IrcServer.objects.get(pk=1)
self.plugin = Markov(self.mock_bot, self.mock_connection, mock.MagicMock())
def test_learn(self):
"""Test that an IRC event triggers learning as expected."""
mock_event = mock.MagicMock()
mock_event.arguments = ['hello this is a test message']
mock_event.target = '#test'
mock_event.recursing = False
with mock.patch('markov.lib.learn_line') as mock_learn_line:
self.plugin.handle_chatter(self.mock_connection, mock_event)
self.assertEqual(mock_learn_line.call_args.args[0], 'hello this is a test message')
def test_learn_self_edit(self):
"""Test that we don't learn our own name when learning something addressed to us."""
mock_event = mock.MagicMock()
mock_event.arguments = ['test_bot: hello this is a test message']
mock_event.target = '#test'
mock_event.recursing = False
with mock.patch('markov.lib.learn_line') as mock_learn_line:
self.plugin.handle_chatter(self.mock_connection, mock_event)
self.assertEqual(mock_learn_line.call_args.args[0], 'hello this is a test message')
def test_learn_variant_self_edit(self):
"""Test that we don't learn our own name when learning something addressed to us, discord style."""
mock_event = mock.MagicMock()
mock_event.arguments = ['@test_bot hello this is a test message']
mock_event.target = '#test'
mock_event.recursing = False
with mock.patch('markov.lib.learn_line') as mock_learn_line:
self.plugin.handle_chatter(self.mock_connection, mock_event)
self.assertEqual(mock_learn_line.call_args.args[0], 'hello this is a test message')
def test_learn_bridge_edit(self):
"""Test that we don't learn the speaker's nick when learning a message from the bridge."""
mock_event = mock.MagicMock()
mock_event.arguments = ['<tester> hello this is a test message']
mock_event.target = '#test'
mock_event.recursing = False
mock_event.source = 'bridge!bridge@localhost'
with mock.patch('markov.lib.learn_line') as mock_learn_line:
self.plugin.handle_chatter(self.mock_connection, mock_event)
self.assertEqual(mock_learn_line.call_args.args[0], 'hello this is a test message')

View File

@ -194,5 +194,6 @@ python_files =
*_tests.py
tests.py
test_*.py
log_level=DEBUG
DJANGO_SETTINGS_MODULE = dr_botzo.settings
django_find_project = false