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 = "" 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 #print(' '.join(chain)) yield from ctx.channel.send(' '.join(chain))