Add random reply chance to markov bot

Whenever someone says something, there's a chance that markov will
interject his opinion. Users can also set the chance between 0.0 and the
default value (in the config) if they want to see markov replies less
often.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-05-30 14:29:37 -07:00
parent 57dd547233
commit 6a1ed5c372
2 changed files with 52 additions and 29 deletions

View File

@@ -3,6 +3,7 @@ from collections import defaultdict
import dataclasses
import json
import logging
import math
from pathlib import Path
import random
from typing import Any, List, Mapping, Sequence
@@ -35,8 +36,9 @@ def windows(items: Sequence[Any], size: int):
@dataclasses.dataclass
class Chain:
def __init__(self, order: int, path: Path):
def __init__(self, order: int, chance: float, path: Path):
self.order = order
self.reply_chance = chance
self.path = path
self.__cache = chain_default()
self.__last_access = 0.0
@@ -85,6 +87,10 @@ class Chain:
return
with open(self.path) as fp:
log.info("Loading markov chain from %s", self.path)
obj = json.load(fp)
# Load the save object
self.reply_chance = obj["reply_chance"]
self.__cache = defaultdict(
chain_inner_default,
{
@@ -95,7 +101,7 @@ class Chain:
for word, weight in value.items()
},
)
for key, value in json.load(fp).items()
for key, value in obj["chain"]
},
)
self.__dirty = False
@@ -106,18 +112,21 @@ class Chain:
if self.__dirty:
log.info("Saving markov chain to %s", self.path)
self.path.parent.mkdir(parents=True, exist_ok=True)
with open(self.path, "w") as fp:
json.dump(
{
# Build the save object
obj = {
"reply_chance": self.reply_chance,
"chain": {
key: {
("" if word is None else word): weight
for word, weight in value.items()
}
for key, value in self.__cache.items()
},
fp,
)
}
with open(self.path, "w") as fp:
json.dump(obj, fp)
self.__dirty = False
if not retain:
log.debug("Pruning markov chain %s from memory", self.path)
self.clear_cache()
@@ -162,6 +171,7 @@ class Markov(Plugin):
self.order = int(self.plugin_config.get("order", 1))
self.data_path = Path(self.plugin_config.get("data_path", "data/markov"))
self.save_every = int(self.plugin_config.get("save_every", 300))
self.reply_chance = float(self.plugin_config.get("reply_chance", 0.01))
self.__chains = {}
self.__save_loop_task = None
self.__saving = asyncio.Lock()
@@ -180,17 +190,22 @@ class Markov(Plugin):
elif line[0] != "!":
# ignore other commands
self.add(channel, who.nick, line)
# also, maybe generate a sentence
chosen = random.random()
chain = self.get_chain(channel, who)
if chosen <= chain.reply_chance:
pass
def get_chain(self, channel: str, who: str) -> Chain:
if channel not in self.__chains:
self.__chains[channel] = {}
if who not in self.__chains[channel]:
path = self.data_path / channel / who
self.__chains[channel][who] = Chain(self.order, path)
self.__chains[channel][who] = Chain(self.order, self.reply_chance, path)
return self.__chains[channel][who]
def add(self, channel: str, who: str, line: str):
if who == self.server_config.nick == who:
if who == self.server_config.nick:
return
chain = self.get_chain(channel, who)
chain.add(line)
@@ -208,7 +223,7 @@ class Markov(Plugin):
if message:
self.send_to(conn, channel, f"{who.nick}: {message}")
case ["force", nick] | ["emulate", nick]:
chain = self.get_chain(channel, who.nick)
chain = self.get_chain(channel, nick)
if not chain:
return
message = chain.generate()
@@ -219,6 +234,13 @@ class Markov(Plugin):
message = chain.generate()
if message:
self.send_to(conn, channel, f"{who.nick}: {message}")
case ["chance", chance]:
chain = self.get_chain(channel, who.nick)
reply_chance = float(chance)
if not math.isnan(reply_chance):
chain.reply_chance = min(
max(float(reply_chance), 0.0), self.reply_chance
)
case _:
# command not recognized
pass

View File

@@ -38,7 +38,8 @@ async def main():
for line in lines:
if mat := LINE_RE.search(line):
name = mat["name"]
message = mat["message"]
message = mat["message"].strip()
if name != server_config.nick and message and message[0] != "!":
plugin.add(channel, name, message)
await plugin.save()