Compare commits

...

3 Commits

Author SHA1 Message Date
419994ee32
add discord bridge field to the channel model
will be used in a future change to clean up markov chains
2023-02-16 16:52:46 -06:00
e27087a86b
linter fixes for markov library methods 2023-02-16 16:29:48 -06:00
8b3caabb57
linting fixes on the markov models 2023-02-16 16:17:49 -06:00
4 changed files with 42 additions and 37 deletions

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.18 on 2023-02-16 22:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ircbot', '0018_ircserver_replace_irc_control_with_markdown'),
]
operations = [
migrations.AddField(
model_name='ircchannel',
name='discord_bridge',
field=models.CharField(blank=True, default='', max_length=32),
),
]

View File

@ -104,6 +104,8 @@ class IrcChannel(models.Model):
markov_learn_from_channel = models.BooleanField(default=True) markov_learn_from_channel = models.BooleanField(default=True)
discord_bridge = models.CharField(default='', max_length=32, blank=True)
class Meta: class Meta:
"""Settings for the model.""" """Settings for the model."""

View File

@ -1,17 +1,16 @@
"""Provide methods for manipulating markov chain processing."""
import logging import logging
import random from random import SystemRandom as sysrand
from django.db.models import Sum from django.db.models import Sum
from markov.models import MarkovContext, MarkovState, MarkovTarget from markov.models import MarkovContext, MarkovState, MarkovTarget
log = logging.getLogger(__name__)
log = logging.getLogger('markov.lib')
def generate_line(context, topics=None, min_words=15, max_words=30, sentence_bias=2, max_tries=5): def generate_line(context, topics=None, min_words=15, max_words=30, sentence_bias=2, max_tries=5):
"""String multiple sentences together into a coherent sentence.""" """Combine multiple sentences together into a coherent sentence."""
tries = 0 tries = 0
line = [] line = []
min_words_per_sentence = min_words / sentence_bias min_words_per_sentence = min_words / sentence_bias
@ -23,7 +22,7 @@ def generate_line(context, topics=None, min_words=15, max_words=30, sentence_bia
else: else:
if len(line) > 0: if len(line) > 0:
if line[-1][-1] not in [',', '.', '!', '?', ':']: if line[-1][-1] not in [',', '.', '!', '?', ':']:
line[-1] += random.choice(['?', '.', '!']) line[-1] += sysrand.choice(['?', '.', '!'])
tries += 1 tries += 1
@ -33,7 +32,6 @@ def generate_line(context, topics=None, min_words=15, max_words=30, sentence_bia
def generate_longish_sentence(context, topics=None, min_words=15, max_words=30, max_tries=100): def generate_longish_sentence(context, topics=None, min_words=15, max_words=30, max_tries=100):
"""Generate a Markov chain, but throw away the short ones unless we get desperate.""" """Generate a Markov chain, but throw away the short ones unless we get desperate."""
sent = "" sent = ""
tries = 0 tries = 0
while tries < max_tries: while tries < max_tries:
@ -52,20 +50,19 @@ def generate_longish_sentence(context, topics=None, min_words=15, max_words=30,
def generate_sentence(context, topics=None, min_words=15, max_words=30): def generate_sentence(context, topics=None, min_words=15, max_words=30):
"""Generate a Markov chain.""" """Generate a Markov chain."""
words = [] words = []
# if we have topics, try to work from it and work backwards # if we have topics, try to work from it and work backwards
if topics: if topics:
topic_word = random.choice(topics) topic_word = sysrand.choice(topics)
topics.remove(topic_word) topics.remove(topic_word)
log.debug("looking for topic '{0:s}'".format(topic_word)) log.debug("looking for topic '%s'", topic_word)
new_states = MarkovState.objects.filter(context=context, v=topic_word) new_states = MarkovState.objects.filter(context=context, v=topic_word)
if len(new_states) > 0: if len(new_states) > 0:
log.debug("found '{0:s}', starting backwards".format(topic_word)) log.debug("found '%s', starting backwards", topic_word)
words.insert(0, topic_word) words.insert(0, topic_word)
while len(words) <= max_words and words[0] != MarkovState._start2: while len(words) <= max_words and words[0] != MarkovState._start2:
log.debug("looking backwards for '{0:s}'".format(words[0])) log.debug("looking backwards for '%s'", words[0])
new_states = MarkovState.objects.filter(context=context, v=words[0]) new_states = MarkovState.objects.filter(context=context, v=words[0])
# if we find a start, use it # if we find a start, use it
if MarkovState._start2 in new_states: if MarkovState._start2 in new_states:
@ -87,7 +84,7 @@ def generate_sentence(context, topics=None, min_words=15, max_words=30):
i = len(words) i = len(words)
while words[-1] != MarkovState._stop: while words[-1] != MarkovState._stop:
log.debug("looking for '{0:s}','{1:s}'".format(words[i-2], words[i-1])) log.debug("looking for '%s','%s'", words[i-2], words[i-1])
new_states = MarkovState.objects.filter(context=context, k1=words[i-2], k2=words[i-1]) new_states = MarkovState.objects.filter(context=context, k1=words[i-2], k2=words[i-1])
log.debug("states retrieved") log.debug("states retrieved")
@ -103,7 +100,7 @@ def generate_sentence(context, topics=None, min_words=15, max_words=30):
words.append(MarkovState._stop) words.append(MarkovState._stop)
elif len(target_hits) > 0: elif len(target_hits) > 0:
# if there's a target word in the states, pick it # if there's a target word in the states, pick it
target_hit = random.choice(target_hits) target_hit = sysrand.choice(target_hits)
log.debug("found a topic hit %s, using it", target_hit) log.debug("found a topic hit %s, using it", target_hit)
topics.remove(target_hit) topics.remove(target_hit)
words.append(target_hit) words.append(target_hit)
@ -129,7 +126,6 @@ def generate_sentence(context, topics=None, min_words=15, max_words=30):
def get_or_create_target_context(target_name): def get_or_create_target_context(target_name):
"""Return the context for a provided nick/channel, creating missing ones.""" """Return the context for a provided nick/channel, creating missing ones."""
target_name = target_name.lower() target_name = target_name.lower()
# find the stuff, or create it # find the stuff, or create it
@ -156,7 +152,6 @@ def get_or_create_target_context(target_name):
def get_word_out_of_states(states, backwards=False): def get_word_out_of_states(states, backwards=False):
"""Pick one random word out of the given states.""" """Pick one random word out of the given states."""
# work around possible broken data, where a k1,k2 should have a value but doesn't # work around possible broken data, where a k1,k2 should have a value but doesn't
if len(states) == 0: if len(states) == 0:
states = MarkovState.objects.filter(v=MarkovState._stop) states = MarkovState.objects.filter(v=MarkovState._stop)
@ -168,9 +163,9 @@ def get_word_out_of_states(states, backwards=False):
# this being None probably means there's no data for this context # this being None probably means there's no data for this context
raise ValueError("no markov states to generate from") raise ValueError("no markov states to generate from")
hit = random.randint(0, count_sum) hit = sysrand.randint(0, count_sum)
log.debug("sum: {0:d} hit: {1:d}".format(count_sum, hit)) log.debug("sum: %s hit: %s", count_sum, hit)
states_itr = states.iterator() states_itr = states.iterator()
for state in states_itr: for state in states_itr:
@ -183,13 +178,12 @@ def get_word_out_of_states(states, backwards=False):
break break
log.debug("found '{0:s}'".format(new_word)) log.debug("found '%s'", new_word)
return new_word return new_word
def learn_line(line, context): def learn_line(line, context):
"""Create a bunch of MarkovStates for a given line of text.""" """Create a bunch of MarkovStates for a given line of text."""
log.debug("learning %s...", line[:40]) log.debug("learning %s...", line[:40])
words = line.split() words = line.split()
@ -200,7 +194,7 @@ def learn_line(line, context):
return return
for i, word in enumerate(words): for i, word in enumerate(words):
log.debug("'{0:s}','{1:s}' -> '{2:s}'".format(words[i], words[i+1], words[i+2])) log.debug("'%s','%s' -> '%s'", words[i], words[i+1], words[i+2])
state, created = MarkovState.objects.get_or_create(context=context, state, created = MarkovState.objects.get_or_create(context=context,
k1=words[i], k1=words[i],
k2=words[i+1], k2=words[i+1],

View File

@ -1,30 +1,22 @@
""" """Save brain pieces as markov chains for chaining."""
markov/models.py --- save brain pieces for chaining
"""
import logging import logging
from django.db import models from django.db import models
log = logging.getLogger(__name__)
log = logging.getLogger('markov.models')
class MarkovContext(models.Model): class MarkovContext(models.Model):
"""Define contexts for Markov chains.""" """Define contexts for Markov chains."""
name = models.CharField(max_length=200, unique=True) name = models.CharField(max_length=200, unique=True)
def __str__(self): def __str__(self):
"""String representation.""" """Provide string representation."""
return "{0:s}".format(self.name) return "{0:s}".format(self.name)
class MarkovTarget(models.Model): class MarkovTarget(models.Model):
"""Define IRC targets that relate to a context, and can occasionally be talked to.""" """Define IRC targets that relate to a context, and can occasionally be talked to."""
name = models.CharField(max_length=200, unique=True) name = models.CharField(max_length=200, unique=True)
@ -33,13 +25,11 @@ class MarkovTarget(models.Model):
chatter_chance = models.IntegerField(default=0) chatter_chance = models.IntegerField(default=0)
def __str__(self): def __str__(self):
"""String representation.""" """Provide string representation."""
return "{0:s} -> {1:s}".format(self.name, self.context.name) return "{0:s} -> {1:s}".format(self.name, self.context.name)
class MarkovState(models.Model): class MarkovState(models.Model):
"""One element in a Markov chain, some text or something.""" """One element in a Markov chain, some text or something."""
_start1 = '__start1' _start1 = '__start1'
@ -54,6 +44,8 @@ class MarkovState(models.Model):
context = models.ForeignKey(MarkovContext, on_delete=models.CASCADE) context = models.ForeignKey(MarkovContext, on_delete=models.CASCADE)
class Meta: class Meta:
"""Options for the model itself."""
index_together = [ index_together = [
['context', 'k1', 'k2'], ['context', 'k1', 'k2'],
['context', 'v'], ['context', 'v'],
@ -65,6 +57,5 @@ class MarkovState(models.Model):
unique_together = ('context', 'k1', 'k2', 'v') unique_together = ('context', 'k1', 'k2', 'v')
def __str__(self): def __str__(self):
"""String representation.""" """Provide string representation."""
return "{0:s},{1:s} -> {2:s} (count: {3:d})".format(self.k1, self.k2, self.v, self.count) return "{0:s},{1:s} -> {2:s} (count: {3:d})".format(self.k1, self.k2, self.v, self.count)