add markov module and irc plugin
just listens to servers right now, doesn't speak up (yet)
This commit is contained in:
parent
7ec643afda
commit
51edb54ed7
@ -40,6 +40,7 @@ INSTALLED_APPS = [
|
|||||||
'bot',
|
'bot',
|
||||||
'dice',
|
'dice',
|
||||||
'logger',
|
'logger',
|
||||||
|
'markov',
|
||||||
'weather',
|
'weather',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
0
markov/__init__.py
Normal file
0
markov/__init__.py
Normal file
7
markov/admin.py
Normal file
7
markov/admin.py
Normal 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
6
markov/apps.py
Normal 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
33
markov/bot.py
Normal 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
30
markov/lib.py
Normal 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
|
41
markov/migrations/0001_initial.py
Normal file
41
markov/migrations/0001_initial.py
Normal 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')},
|
||||||
|
),
|
||||||
|
]
|
0
markov/migrations/__init__.py
Normal file
0
markov/migrations/__init__.py
Normal file
43
markov/models.py
Normal file
43
markov/models.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user