add markov module and irc plugin

just listens to servers right now, doesn't speak up (yet)
This commit is contained in:
Brian S. Stephan 2018-01-10 09:38:12 -06:00
parent 7ec643afda
commit 51edb54ed7
9 changed files with 161 additions and 0 deletions

View File

@ -40,6 +40,7 @@ INSTALLED_APPS = [
'bot',
'dice',
'logger',
'markov',
'weather',
]

0
markov/__init__.py Normal file
View File

7
markov/admin.py Normal file
View File

@ -0,0 +1,7 @@
"""Display Markov data elements in the Django admin."""
from django.contrib import admin
from markov.models import MarkovContext, MarkovState
admin.site.register(MarkovContext)
admin.site.register(MarkovState)

6
markov/apps.py Normal file
View File

@ -0,0 +1,6 @@
"""App config for the markov module."""
from django.apps import AppConfig
class MarkovConfig(AppConfig):
name = 'markov'

33
markov/bot.py Normal file
View File

@ -0,0 +1,33 @@
"""Observe Markov chains from Discord."""
import logging
from bot import hitomi
from markov import lib
logger = logging.getLogger(__name__)
logger.info("loading markov plugin")
def on_message(message):
"""Keep the observed Markov chains."""
# ignore self
if message.author == hitomi.user:
logger.debug("markov ignoring message authored by self")
return
if message.channel.is_private:
# DMs have a context of the author
context_name = message.author
else:
# channels have a context of the server, but we should ignore channels that override @everyone's read ability
for changed_role in message.channel.changed_roles:
if changed_role.is_everyone:
if not changed_role.permissions.read_messages:
logger.debug("markov ignoring channel that @everyone can't read")
return
context_name = message.server.id
lib.learn_line(message.content, context_name)
hitomi.on_message_handlers.append(on_message)

30
markov/lib.py Normal file
View File

@ -0,0 +1,30 @@
"""Shared methods for the Markov module."""
import logging
from markov.models import MarkovContext, MarkovState
logger = logging.getLogger(__name__)
def learn_line(line, context_name):
"""Create a bunch of MarkovStates for a given line of text."""
logger.debug("learning %s...", line[:40])
context, created = MarkovContext.objects.get_or_create(name=context_name)
words = line.split()
words = [MarkovState.start1, MarkovState.start2] + words + [MarkovState.stop]
for word in words:
if len(word) > MarkovState._meta.get_field('k1').max_length:
return
for i, word in enumerate(words):
logger.debug("'{0:s}','{1:s}' -> '{2:s}'".format(words[i], words[i + 1], words[i + 2]))
state, created = MarkovState.objects.get_or_create(context=context, k1=words[i], k2=words[i + 1],
v=words[i + 2])
state.count += 1
state.save()
if i > len(words) - 4:
break

View File

@ -0,0 +1,41 @@
# Generated by Django 2.0.1 on 2018-01-09 19:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='MarkovContext',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, unique=True)),
],
),
migrations.CreateModel(
name='MarkovState',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('k1', models.CharField(max_length=256)),
('k2', models.CharField(max_length=256)),
('v', models.CharField(max_length=256)),
('count', models.IntegerField(default=0)),
('context', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='markov.MarkovContext')),
],
),
migrations.AlterUniqueTogether(
name='markovstate',
unique_together={('context', 'k1', 'k2', 'v')},
),
migrations.AlterIndexTogether(
name='markovstate',
index_together={('context', 'k1', 'k2'), ('context', 'v')},
),
]

View File

43
markov/models.py Normal file
View File

@ -0,0 +1,43 @@
"""Database objects for storing Markov chains."""
import logging
from django.db import models
log = logging.getLogger(__name__)
class MarkovContext(models.Model):
"""Define contexts for Markov chains."""
name = models.CharField(max_length=200, unique=True)
def __str__(self):
"""String representation."""
return "{0:s}".format(self.name)
class MarkovState(models.Model):
"""One element in a Markov chain, some text or something."""
start1 = '__start1'
start2 = '__start2'
stop = '__stop'
k1 = models.CharField(max_length=256)
k2 = models.CharField(max_length=256)
v = models.CharField(max_length=256)
count = models.IntegerField(default=0)
context = models.ForeignKey(MarkovContext, on_delete=models.CASCADE)
class Meta:
index_together = [
['context', 'k1', 'k2'],
['context', 'v'],
]
unique_together = ('context', 'k1', 'k2', 'v')
def __str__(self):
"""String representation."""
return "{0:s},{1:s} -> {2:s} (count: {3:d})".format(self.k1, self.k2, self.v, self.count)