Chatbot v1

This commit is contained in:
JP
2018-07-01 18:15:45 +02:00
parent de4aa44201
commit c2b7cf7338
4 changed files with 132 additions and 2 deletions
+126
View File
@@ -0,0 +1,126 @@
import asyncio
import discord
from discord.ext import commands
import numpy as np
class Chatbot(object):
"""Een heuse chatbot. Under construction..."""
def __init__(self, bot):
self.bot = bot
self.voice_states = {}
self.END_OF_MESSAGE = "<EOM>"
try:
np.load('markov_dict.npy')
except FileNotFoundError:
#If there's no initial file, make a new one:
initFile = {'Hoi':{self.END_OF_MESSAGE : 1}}
np.save("markov_dict.npy", initFile)
except:
print("ERROR! - Could not open or create Markov chat file")
def __check(self, ctx):
return True
#TODO laat Stroop reageren op persoonlijke berichten
#Make word pairs:
def makePairs(self, corpus):
for i in range(len(corpus)-1):
yield (corpus[i], corpus[i+1])
#Learn from a human message
#Returns an updated dict
def learnFrom(self, initialWordDict, message: str):
newWordDict = initialWordDict
message = message + " " + self.END_OF_MESSAGE
corpus = message.split()
pairs = self.makePairs(corpus)
#Add wordcounts to dict
for word_1, word_2 in pairs:
if word_1 in newWordDict.keys():
if word_2 in newWordDict[word_1].keys():
newWordDict[word_1][word_2] += 1
else:
newWordDict[word_1][word_2] = 1
else:
newWordDict[word_1] = {word_2 : 1}
return newWordDict;
@commands.command(pass_context=True, hidden=True)
@asyncio.coroutine
def trainmarkovchat(self, ctx, channelId=409751773595566081, nrOfMessages=50):
"""Train that chain, jermaine
param channelId: use this channel to train
param nrOfMessages: use the x most recent messages
"""
channel=self.bot.get_channel(channelId)
#Open current chain:
wordDict = np.load('markov_dict.npy').item()
#Iterate through the last x messages from the channel:
iterator = channel.history(limit=nrOfMessages, before=None, after=None, reverse=False, around=None)
for i in range(nrOfMessages):
msg = yield from iterator.next()
messageContent = msg.content.strip()
if messageContent == "":
pass #print("-- Bericht is leeg. Negeer.")
elif msg.author.bot:
pass #print("-- Dit is een bot chat. Negeer.")
elif messageContent[0] == "!":
pass #print("-- Dit is een commando. Negeer.")
elif "```" in messageContent:
pass #print("-- Daar zit een codeblok in. Negeer.")
else:
wordDict = self.learnFrom(wordDict, messageContent)
np.save("markov_dict.npy", wordDict)
yield from ctx.channel.send("Ik heb weer wat nieuwe woordjes geleerd. :slight_smile:")
@commands.command(pass_context=True, hidden=False)
@asyncio.coroutine
def reageer(self, ctx):
"""UNDER CONSTRUCTION. Laat Stroop iets willekeurigs zeggen.
Stroop leert van onze gesprekken. Hij kan zich nu voordoen als een van ons. Soort van.
"""
MAX_N_WORDS = 100 #Don't use more words than this
#Get dictionary/Markov chain
wordDict = np.load('markov_dict.npy').item()
#TODO store in global variable to minimise IO?
#Delete command
try:
yield from ctx.message.delete()
except:
print("No permission to delete message")
#print(wordDict)
#Pick a random first word (Which is not an end-of-message)
#TODO pick a word which responds to the previous message in the channel
first_word = np.random.choice(list(wordDict.keys()))
while first_word == self.END_OF_MESSAGE: #or first_word.islower() ???
first_word = np.random.choice(list(wordDict.keys()))
chain = [first_word]
#Go through the chain:
for i in range(MAX_N_WORDS):
if chain[-1] == self.END_OF_MESSAGE:
break
#Calculate probabilities and pick a next word:
#https://stackoverflow.com/questions/835092/python-dictionary-are-keys-and-values-always-the-same-order
possible_words = list(wordDict[chain[-1]].keys())
weights = np.array(list(wordDict[chain[-1]].values()))
probs = weights / weights.sum()
chain.append(np.random.choice(possible_words, p=probs))
chain = chain[:-1] #remove <EOM>
#print(' '.join(chain))
yield from ctx.channel.send(' '.join(chain))